]> code.delx.au - gnu-emacs/blobdiff - lisp/erc/erc.el
* term/ns-win.el (ns-alternatives-map, ns-insert-working-text)
[gnu-emacs] / lisp / erc / erc.el
index 4317b831d56c9c0f38e4c0050b1e44f5170bc6cb..5066efa3bedd29d7b5b3285511469cdccbaa8293 100644 (file)
@@ -1,8 +1,7 @@
 ;; erc.el --- An Emacs Internet Relay Chat client
 
 ;; Copyright (C) 1997, 1998, 1999, 2000, 2001, 2002, 2003, 2004, 2005,
-;;   2006 Free Software Foundation, Inc.
-;; Copyright (C) 2004 Brian Palmer
+;;   2006, 2007, 2008, 2009 Free Software Foundation, Inc.
 
 ;; Author: Alexander L. Belikoff (alexander@belikoff.net)
 ;; Contributors: Sergey Berezin (sergey.berezin@cs.cmu.edu),
 
 ;; This file is part of GNU Emacs.
 
-;; GNU Emacs is free software; you can redistribute it and/or modify
+;; GNU Emacs is free software: you can redistribute it and/or modify
 ;; it under the terms of the GNU General Public License as published by
-;; the Free Software Foundation; either version 2, or (at your option)
-;; any later version.
+;; the Free Software Foundation, either version 3 of the License, or
+;; (at your option) any later version.
 
 ;; GNU Emacs is distributed in the hope that it will be useful,
 ;; but WITHOUT ANY WARRANTY; without even the implied warranty of
 ;; GNU General Public License for more details.
 
 ;; You should have received a copy of the GNU General Public License
-;; along with GNU Emacs; see the file COPYING.  If not, write to the
-;; Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
-;; Boston, MA 02110-1301, USA.
+;; along with GNU Emacs.  If not, see <http://www.gnu.org/licenses/>.
 
 ;;; Commentary:
 
-;; ERC is an IRC client for Emacs.
+;; ERC is a powerful, modular, and extensible IRC client for Emacs.
 
 ;; For more information, see the following URLs:
 ;; * http://sv.gnu.org/projects/erc/
-;; * http://www.emacswiki.org/cgi-bin/wiki.pl?EmacsIRCClient
+;; * http://www.emacswiki.org/cgi-bin/wiki/ERC
 
 ;; As of 2006-06-13, ERC development is now hosted on Savannah
 ;; (http://sv.gnu.org/projects/erc).  I invite everyone who wants to
 
 ;; To connect to an IRC server, do
 ;;
-;; M-x erc-select RET
+;; M-x erc RET
 ;;
 ;; After you are connected to a server, you can use C-h m or have a look at
-;; the IRC menu.
+;; the ERC menu.
 
 ;;; History:
 ;;
 
 ;;; Code:
 
-(defconst erc-version-string "Version 5.1.4"
+(defconst erc-version-string "Version 5.3"
   "ERC version.  This is used by function `erc-version'.")
 
 (eval-when-compile (require 'cl))
@@ -75,7 +72,6 @@
 (require 'pp)
 (require 'thingatpt)
 (require 'erc-compat)
-(require 'erc-menu)
 
 (defvar erc-official-location
   "http://emacswiki.org/cgi-bin/wiki/ERC (mailing list: erc-discuss@gnu.org)"
@@ -157,8 +153,8 @@ parameters and authentication."
 This can be either a string or a number."
   :group 'erc
   :type '(choice (const :tag "None" nil)
-                (const :tag "Port number" number)
-                (const :tag "Port string" string)))
+                (integer :tag "Port number")
+                (string :tag "Port string")))
 
 (defcustom erc-nick nil
   "Nickname to use if one is not provided.
@@ -326,7 +322,7 @@ Each function should accept two arguments, NEW-NICK and OLD-NICK."
 
 (defcustom erc-connect-pre-hook '(erc-initialize-log-marker)
   "Hook called just before `erc' calls `erc-connect'.
-Functions are run in the buffer-to-be."
+Functions are passed a buffer as the first argument."
   :group 'erc-hooks
   :type 'hook)
 
@@ -357,6 +353,17 @@ nicknames with erc-server-user struct instances.")
              (cdr (assq (aref s (match-beginning 0)) c)))))
     s))
 
+(defmacro erc-with-server-buffer (&rest body)
+  "Execute BODY in the current ERC server buffer.
+If no server buffer exists, return nil."
+  (let ((buffer (make-symbol "buffer")))
+    `(let ((,buffer (erc-server-buffer)))
+       (when (buffer-live-p ,buffer)
+        (with-current-buffer ,buffer
+          ,@body)))))
+(put 'erc-with-server-buffer 'lisp-indent-function 0)
+(put 'erc-with-server-buffer 'edebug-form-spec '(body))
+
 (defstruct (erc-server-user (:type vector) :named)
   ;; User data
   nickname host login full-name info
@@ -383,14 +390,14 @@ in the current buffer's `erc-channel-users' hash table."
 (defsubst erc-get-server-user (nick)
   "Finds the USER corresponding to NICK in the current server's
 `erc-server-users' hash table."
-  (with-current-buffer (process-buffer erc-server-process)
+  (erc-with-server-buffer
     (gethash (erc-downcase nick) erc-server-users)))
 
 (defsubst erc-add-server-user (nick user)
   "This function is for internal use only.
 
 Adds USER with nickname NICK to the `erc-server-users' hash table."
-  (with-current-buffer (process-buffer erc-server-process)
+  (erc-with-server-buffer
     (puthash (erc-downcase nick) user erc-server-users)))
 
 (defsubst erc-remove-server-user (nick)
@@ -401,7 +408,7 @@ hash table.  This user is not removed from the
 `erc-channel-users' lists of other buffers.
 
 See also: `erc-remove-user'."
-  (with-current-buffer (process-buffer erc-server-process)
+  (erc-with-server-buffer
     (remhash (erc-downcase nick) erc-server-users)))
 
 (defun erc-change-user-nickname (user new-nick)
@@ -412,7 +419,7 @@ Changes the nickname of USER to NEW-NICK in the
 other buffers are also changed."
   (let ((nick (erc-server-user-nickname user)))
     (setf (erc-server-user-nickname user) new-nick)
-    (with-current-buffer (process-buffer erc-server-process)
+    (erc-with-server-buffer
       (remhash (erc-downcase nick) erc-server-users)
       (puthash (erc-downcase new-nick) user erc-server-users))
     (dolist (buf (erc-server-user-buffers user))
@@ -474,7 +481,7 @@ Removes all users in the current channel.  This is called by
     (clrhash erc-channel-users)))
 
 (defsubst erc-channel-user-op-p (nick)
-  "Return `t' if NICK is an operator in the current channel."
+  "Return t if NICK is an operator in the current channel."
   (and nick
        (hash-table-p erc-channel-users)
        (let ((cdata (erc-get-channel-user nick)))
@@ -482,7 +489,7 @@ Removes all users in the current channel.  This is called by
              (erc-channel-user-op (cdr cdata))))))
 
 (defsubst erc-channel-user-voice-p (nick)
-  "Return `t' if NICK has voice in the current channel."
+  "Return t if NICK has voice in the current channel."
   (and nick
        (hash-table-p erc-channel-users)
        (let ((cdata (erc-get-channel-user nick)))
@@ -505,16 +512,15 @@ See also: `erc-sort-channel-users-by-activity'"
 
 (defun erc-get-server-nickname-list ()
   "Returns a list of known nicknames on the current server."
-    (if (erc-server-process-alive)
-       (with-current-buffer (erc-server-buffer)
-         (let (nicks)
-           (when (hash-table-p erc-server-users)
-             (maphash (lambda (n user)
-                        (setq nicks
-                              (cons (erc-server-user-nickname user)
-                                    nicks)))
-                      erc-server-users)
-             nicks)))))
+  (erc-with-server-buffer
+    (let (nicks)
+      (when (hash-table-p erc-server-users)
+       (maphash (lambda (n user)
+                  (setq nicks
+                        (cons (erc-server-user-nickname user)
+                              nicks)))
+                erc-server-users)
+       nicks))))
 
 (defun erc-get-channel-nickname-list ()
   "Returns a list of known nicknames on the current channel."
@@ -529,16 +535,15 @@ See also: `erc-sort-channel-users-by-activity'"
 
 (defun erc-get-server-nickname-alist ()
   "Returns an alist of known nicknames on the current server."
-    (if (erc-server-process-alive)
-       (with-current-buffer (erc-server-buffer)
-         (let (nicks)
-           (when (hash-table-p erc-server-users)
-             (maphash (lambda (n user)
-                        (setq nicks
-                              (cons (cons (erc-server-user-nickname user) nil)
-                                    nicks)))
-                      erc-server-users)
-             nicks)))))
+  (erc-with-server-buffer
+    (let (nicks)
+      (when (hash-table-p erc-server-users)
+       (maphash (lambda (n user)
+                  (setq nicks
+                        (cons (cons (erc-server-user-nickname user) nil)
+                              nicks)))
+                erc-server-users)
+       nicks))))
 
 (defun erc-get-channel-nickname-alist ()
   "Returns an alist of known nicknames on the current channel."
@@ -774,7 +779,7 @@ set if some hacker is trying to flood you away."
   :type 'boolean)
 
 (defcustom erc-prompt-for-channel-key nil
-  "Prompt for channel key when using `erc-join-channel' interactively"
+  "Prompt for channel key when using `erc-join-channel' interactively."
   :group 'erc
   :type 'boolean)
 
@@ -783,6 +788,13 @@ set if some hacker is trying to flood you away."
   :group 'erc
   :type 'string)
 
+(defcustom erc-system-name nil
+  "Use this as the name of your system.
+If nil, ERC will call `system-name' to get this information."
+  :group 'erc
+  :type '(choice (const :tag "Default system name" nil)
+                string))
+
 (defcustom erc-ignore-list nil
   "*List of regexps matching user identifiers to ignore.
 
@@ -807,7 +819,7 @@ whose identifier matches, the message will not be processed.
 CAVEAT: ERC doesn't know about the user and host of anyone who
 was already in the channel when you joined, but never said
 anything, so it won't be able to match the user and host of those
-people. You can update the ERC internal info using /WHO *."
+people.  You can update the ERC internal info using /WHO *."
   :group 'erc-ignore
   :type '(repeat regexp))
 
@@ -822,13 +834,15 @@ See `erc-server-flood-margin' for other flood-related parameters.")
 ;; Script parameters
 
 (defcustom erc-startup-file-list
-  '("~/.ercrc.el" "~/.ercrc" ".ercrc.el" ".ercrc")
+  (list (concat erc-user-emacs-directory ".ercrc.el")
+       (concat erc-user-emacs-directory ".ercrc")
+       "~/.ercrc.el" "~/.ercrc" ".ercrc.el" ".ercrc")
   "List of files to try for a startup script.
 The first existent and readable one will get executed.
 
-If the filename ends with `.el' it is presumed to be an emacs-lisp
-script and it gets (load)ed.  Otherwise is is treated as a bunch of
-regular IRC commands"
+If the filename ends with `.el' it is presumed to be an Emacs Lisp
+script and it gets (load)ed.  Otherwise it is treated as a bunch of
+regular IRC commands."
   :group 'erc-scripts
   :type '(repeat file))
 
@@ -840,7 +854,7 @@ directory in the list."
   :type '(repeat directory))
 
 (defcustom erc-script-echo t
-  "*If not-NIL, echo the IRC script commands locally."
+  "*If non-nil, echo the IRC script commands locally."
   :group 'erc-scripts
   :type 'boolean)
 
@@ -1041,7 +1055,7 @@ At this point, all modifications from prior hook functions are done.
 NOTE: The functions on this hook are called _before_ sending a command
 to the server.
 
-This function is called with narrowing, ala `erc-send-modify-hook'"
+This function is called with narrowing, ala `erc-send-modify-hook'."
   :group 'erc-hooks
   :type 'hook
   :options '(erc-make-read-only))
@@ -1094,6 +1108,7 @@ which the local user typed."
     (define-key map "\C-c\C-r" 'erc-remove-text-properties-region)
     (define-key map "\C-c\C-t" 'erc-set-topic)
     (define-key map "\C-c\C-u" 'erc-kill-input)
+    (define-key map "\C-c\C-x" 'erc-quit-server)
     (define-key map "\M-\t" 'ispell-complete-word)
     (define-key map "\t" 'erc-complete-word)
 
@@ -1151,7 +1166,13 @@ This will only be used if `erc-header-line-face-method' is non-nil."
 See the variable `erc-command-indicator'."
   :group 'erc-faces)
 
-(defface erc-notice-face '((t (:bold t :foreground "SlateBlue")))
+(defface erc-notice-face
+  (if (or (featurep 'xemacs)
+         (< emacs-major-version 22))
+      '((t (:bold t :foreground "blue")))
+    '((((class color) (min-colors 88))
+       (:bold t :foreground "SlateBlue"))
+      (t (:bold t :foreground "blue"))))
   "ERC face for notices."
   :group 'erc-faces)
 
@@ -1163,6 +1184,12 @@ See the variable `erc-command-indicator'."
   "ERC face for errors."
   :group 'erc-faces)
 
+;; same default color as `erc-input-face'
+(defface erc-my-nick-face '((t (:bold t :foreground "brown")))
+  "ERC face for your current nickname in messages sent by you.
+See also `erc-show-my-nick'."
+  :group 'erc-faces)
+
 (defface erc-nick-default-face '((t (:bold t)))
   "ERC nickname default face."
   :group 'erc-faces)
@@ -1191,7 +1218,7 @@ DOC is the documentation string to use for the minor mode.
 ENABLE-BODY is a list of expressions used to enable the mode.
 DISABLE-BODY is a list of expressions used to disable the mode.
 If LOCAL-P is non-nil, the mode will be created as a buffer-local
-mode.  Rather than a global one.
+mode, rather than a global one.
 
 This will define a minor mode called erc-NAME-mode, possibly
 an alias erc-ALIAS-mode, as well as the helper functions
@@ -1259,7 +1286,7 @@ You should make sure that `current-buffer' is a server buffer.
 This function temporarily adds a function to EVENT's hook to
 execute FORMS.  After FORMS are run, the function is removed from
 EVENT's hook.  The last expression of FORMS should be either nil
-or t nil indicates that the other functions on EVENT's hook
+or t, where nil indicates that the other functions on EVENT's hook
 should be run too, and t indicates that other functions should
 not be run.
 
@@ -1270,7 +1297,7 @@ capabilities."
   (unless (erc-server-buffer-p)
     (error
      "You should only run `erc-once-with-server-event' in a server buffer"))
-  (let ((fun (erc-gensym))
+  (let ((fun (make-symbol "fun"))
        (hook (erc-get-hook event)))
      (put fun 'erc-original-buffer (current-buffer))
      (fset fun `(lambda (proc parsed)
@@ -1287,13 +1314,13 @@ capabilities."
 This function temporarily prepends a function to EVENT's hook to
 execute FORMS.  After FORMS are run, the function is removed from
 EVENT's hook.  The last expression of FORMS should be either nil
-or t nil indicates that the other functions on EVENT's hook
+or t, where nil indicates that the other functions on EVENT's hook
 should be run too, and t indicates that other functions should
 not be run.
 
 When FORMS execute, the current buffer is the server buffer associated with the
 connection over which the data was received that triggered EVENT."
-  (let ((fun (erc-gensym))
+  (let ((fun (make-symbol "fun"))
        (hook (erc-get-hook event)))
      (fset fun `(lambda (proc parsed)
                  (remove-hook ',hook ',fun)
@@ -1315,8 +1342,7 @@ the process buffer."
        (process-buffer erc-server-process)))
 
 (defun erc-server-buffer-live-p ()
-  "Return t if the buffer associated with `erc-server-process'
-has not been killed."
+  "Return t if the server buffer has not been killed."
   (and (processp erc-server-process)
        (buffer-live-p (process-buffer erc-server-process))))
 
@@ -1328,6 +1354,14 @@ If BUFFER is nil, the current buffer is used."
     (and (eq major-mode 'erc-mode)
         (null (erc-default-target)))))
 
+(defun erc-open-server-buffer-p (&optional buffer)
+  "Return non-nil if argument BUFFER is an ERC server buffer that
+has an open IRC process.
+
+If BUFFER is nil, the current buffer is used."
+  (and (erc-server-buffer-p)
+       (erc-server-process-alive)))
+
 (defun erc-query-buffer-p (&optional buffer)
   "Return non-nil if BUFFER is an ERC query buffer.
 If BUFFER is nil, the current buffer is used."
@@ -1340,7 +1374,7 @@ If BUFFER is nil, the current buffer is used."
 (defun erc-ison-p (nick)
   "Return non-nil if NICK is online."
   (interactive "sNick: ")
-  (with-current-buffer (erc-server-buffer)
+  (erc-with-server-buffer
     (let ((erc-online-p 'unknown))
       (erc-once-with-server-event
        303
@@ -1384,13 +1418,13 @@ If BUFFER is nil, the current buffer is used."
 (defvar erc-active-buffer nil
   "The current active buffer, the one where the user typed the last command.
 Defaults to the server buffer, and should only be set in the
-server buffer")
+server buffer.")
 (make-variable-buffer-local 'erc-active-buffer)
 
 (defun erc-active-buffer ()
   "Return the value of `erc-active-buffer' for the current server.
 Defaults to the server buffer."
-  (with-current-buffer (erc-server-buffer)
+  (erc-with-server-buffer
     (if (buffer-live-p erc-active-buffer)
        erc-active-buffer
       (setq erc-active-buffer (current-buffer)))))
@@ -1432,26 +1466,31 @@ Turning on `erc-mode' runs the hook `erc-mode-hook'."
 (defconst erc-default-server "irc.freenode.net"
   "IRC server to use if it cannot be detected otherwise.")
 
-(defconst erc-default-port "ircd"
+(defconst erc-default-port 6667
   "IRC port to use if it cannot be detected otherwise.")
 
 (defcustom erc-join-buffer 'buffer
-  "Determines how to display the newly created IRC buffer.
-'window - in another window,
-'window-noselect - in another window, but don't select that one,
-'frame - in another frame,
-'bury - bury it in a new buffer,
-any other value - in place of the current buffer"
+  "Determines how to display a newly created IRC buffer.
+
+The available choices are:
+
+  'window          - in another window,
+  'window-noselect - in another window, but don't select that one,
+  'frame           - in another frame,
+  'bury            - bury it in a new buffer,
+  'buffer          - in place of the current buffer,
+  any other value  - in place of the current buffer."
   :group 'erc-buffers
-  :type '(choice (const window)
-                (const window-noselect)
-                (const frame)
-                (const bury)
-                (const buffer)))
+  :type '(choice (const :tag "Split window and select" window)
+                (const :tag "Split window, don't select" window-noselect)
+                (const :tag "New frame" frame)
+                (const :tag "Bury in new buffer" bury)
+                (const :tag "Use current buffer" buffer)
+                (const :tag "Use current buffer" t)))
 
 (defcustom erc-frame-alist nil
   "*Alist of frame parameters for creating erc frames.
-A value of `nil means to use `default-frame-alist'."
+A value of nil means to use `default-frame-alist'."
   :group 'erc-buffers
   :type '(repeat (cons :format "%v"
                       (symbol :tag "Parameter")
@@ -1463,6 +1502,14 @@ This only has effect when `erc-join-buffer' is set to `frame'."
   :group 'erc-buffers
   :type 'boolean)
 
+(defcustom erc-reuse-frames t
+  "*Determines whether new frames are always created.
+Non-nil means that a new frame is not created to display an ERC
+buffer if there is already a window displaying it.  This only has
+effect when `erc-join-buffer' is set to `frame'."
+  :group 'erc-buffers
+  :type 'boolean)
+
 (defun erc-channel-p (channel)
   "Return non-nil if CHANNEL seems to be an IRC channel name."
   (cond ((stringp channel)
@@ -1533,7 +1580,7 @@ symbol, it may have these values:
             (or target
                 (with-current-buffer (get-buffer buf-name)
                   (and (erc-server-buffer-p)
-                       (not erc-server-connected))))
+                       (not (erc-server-process-alive)))))
             (with-current-buffer (get-buffer buf-name)
               (and (string= erc-session-server server)
                    (erc-port-equal erc-session-port port))))
@@ -1554,7 +1601,8 @@ All strings are compared according to IRC protocol case rules, see
   (catch 'result
     (while list
       (if (string= string (erc-downcase (car list)))
-         (throw 'result list) (setq list (cdr list))))))
+         (throw 'result list)
+       (setq list (cdr list))))))
 
 (defmacro erc-with-buffer (spec &rest body)
   "Execute BODY in the buffer associated with SPEC.
@@ -1566,16 +1614,16 @@ SPEC should have the form
 If TARGET is a buffer, use it.  Otherwise, use the buffer
 matching TARGET in the process specified by PROCESS.
 
-If PROCESS is nil, use the current `erc-server-process'
+If PROCESS is nil, use the current `erc-server-process'.
 See `erc-get-buffer' for details.
 
 See also `with-current-buffer'.
 
 \(fn (TARGET [PROCESS]) BODY...)"
-  (let ((buf (erc-gensym))
-       (proc (erc-gensym))
-       (target (erc-gensym))
-       (process (erc-gensym)))
+  (let ((buf (make-symbol "buf"))
+       (proc (make-symbol "proc"))
+       (target (make-symbol "target"))
+       (process (make-symbol "process")))
     `(let* ((,target ,(car spec))
            (,process ,(cadr spec))
            (,buf (if (bufferp ,target)
@@ -1585,7 +1633,7 @@ See also `with-current-buffer'.
                                          erc-server-process))))
                      (if (and ,target ,proc)
                          (erc-get-buffer ,target ,proc))))))
-       (when ,buf
+       (when (buffer-live-p ,buf)
         (with-current-buffer ,buf
           ,@body)))))
 (put 'erc-with-buffer 'lisp-indent-function 1)
@@ -1640,18 +1688,26 @@ FORMS will be evaluated in all buffers having the process PROCESS and
 where PRED matches or in all buffers of the server process if PRED is
 nil."
   ;; Make the evaluation have the correct order
-  (let ((pre (erc-gensym))
-       (pro (erc-gensym)))
-    `(let ((,pro ,process)
-          (,pre ,pred))
-       (mapcar (lambda (buffer)
-                (with-current-buffer buffer
-                  ,@forms))
-              (erc-buffer-list ,pre
-                               ,pro)))))
+  (let ((pre (make-symbol "pre"))
+       (pro (make-symbol "pro")))
+    `(let* ((,pro ,process)
+           (,pre ,pred)
+           (res (mapcar (lambda (buffer)
+                          (with-current-buffer buffer
+                            ,@forms))
+                        (erc-buffer-list ,pre
+                                         ,pro))))
+       ;; Silence the byte-compiler by binding the result of mapcar to
+       ;; a variable.
+       res)))
 (put 'erc-with-all-buffers-of-server 'lisp-indent-function 1)
 (put 'erc-with-all-buffers-of-server 'edebug-form-spec '(form form body))
 
+;; (iswitchb-mode) will autoload iswitchb.el
+(defvar iswitchb-temp-buflist)
+(declare-function iswitchb-read-buffer "iswitchb"
+                (prompt &optional default require-match start matches-set))
+
 (defun erc-iswitchb (&optional arg)
   "Use `iswitchb-read-buffer' to prompt for a ERC buffer to switch to.
 When invoked with prefix argument, use all erc buffers.  Without prefix
@@ -1662,23 +1718,24 @@ If `erc-track-mode' is in enabled, put the last element of
 Due to some yet unresolved reason, global function `iswitchb-mode'
 needs to be active for this function to work."
   (interactive "P")
-  (eval-when-compile
-    (require 'iswitchb))
-  (let ((iswitchb-make-buflist-hook
-        (lambda ()
-          (setq iswitchb-temp-buflist
-                (mapcar 'buffer-name
-                        (erc-buffer-list
-                         nil
-                         (when (and arg (boundp 'erc-server-process))
-                           erc-server-process)))))))
-    (switch-to-buffer
-     (iswitchb-read-buffer
-      "Switch-to: "
-      (if (boundp 'erc-modified-channels-alist)
-         (buffer-name (caar (last erc-modified-channels-alist)))
-       nil)
-      t))))
+  (let ((enabled (bound-and-true-p iswitchb-mode)))
+    (or enabled (iswitchb-mode 1))
+    (unwind-protect
+       (let ((iswitchb-make-buflist-hook
+              (lambda ()
+                (setq iswitchb-temp-buflist
+                      (mapcar 'buffer-name
+                              (erc-buffer-list
+                               nil
+                               (when arg erc-server-process)))))))
+         (switch-to-buffer
+          (iswitchb-read-buffer
+           "Switch-to: "
+           (if (boundp 'erc-modified-channels-alist)
+               (buffer-name (caar (last erc-modified-channels-alist)))
+             nil)
+           t)))
+      (or enabled (iswitchb-mode -1)))))
 
 (defun erc-channel-list (proc)
   "Return a list of channel buffers.
@@ -1721,7 +1778,10 @@ all channel buffers on all servers."
 (make-variable-buffer-local 'erc-invitation)
 
 (defvar erc-away nil
-  "Non-nil indicates that we are away.")
+  "Non-nil indicates that we are away.
+
+Use `erc-away-time' to access this if you might be in a channel
+buffer rather than a server buffer.")
 (make-variable-buffer-local 'erc-away)
 
 (defvar erc-channel-list nil
@@ -1754,9 +1814,9 @@ all channel buffers on all servers."
             mods))))
 
 (defcustom erc-modules '(netsplit fill button match track completion readonly
-                                 ring autojoin noncommands irccontrols
-                                 stamp list)
-  "A list of modules which erc should enable.
+                        networks ring autojoin noncommands irccontrols
+                        move-to-prompt stamp menu list)
+  "A list of modules which ERC should enable.
 If you set the value of this without using `customize' remember to call
 \(erc-update-modules) after you change it.  When using `customize', modules
 removed from the list will be disabled."
@@ -1779,40 +1839,50 @@ removed from the list will be disabled."
   :type
   '(set
     :greedy t
-    (const :tag "Set away status automatically" autoaway)
-    (const :tag "Join channels automatically" autojoin)
-    (const :tag "Buttonize URLs, nicknames, and other text" button)
-    (const :tag "Wrap long lines" fill)
-    (const :tag "Launch an identd server on port 8113" identd)
-    (const :tag "Highlight or remove IRC control characters"
+    (const :tag "autoaway: Set away status automatically" autoaway)
+    (const :tag "autojoin: Join channels automatically" autojoin)
+    (const :tag "button: Buttonize URLs, nicknames, and other text" button)
+    (const :tag "capab: Mark unidentified users on servers supporting CAPAB"
+          capab-identify)
+    (const :tag "completion: Complete nicknames and commands (programmable)"
+          completion)
+    (const :tag "hecomplete: Complete nicknames and commands (old)" hecomplete)
+    (const :tag "dcc: Provide Direct Client-to-Client support" dcc)
+    (const :tag "fill: Wrap long lines" fill)
+    (const :tag "identd: Launch an identd server on port 8113" identd)
+    (const :tag "irccontrols: Highlight or remove IRC control characters"
           irccontrols)
-    (const :tag "List channels in a separate buffer" list)
-    (const :tag "Save buffers in logs" log)
-    (const :tag "Highlight pals, fools, and other keywords" match)
-    (const :tag "Detect netsplits" netsplit)
-    (const :tag "Don't display non-IRC commands after evaluation"
+    (const :tag "keep-place: Leave point above un-viewed text" keep-place)
+    (const :tag "list: List channels in a separate buffer" list)
+    (const :tag "log: Save buffers in logs" log)
+    (const :tag "match: Highlight pals, fools, and other keywords" match)
+    (const :tag "menu: Display a menu in ERC buffers" menu)
+    (const :tag "move-to-prompt: Move to the prompt when typing text"
+          move-to-prompt)
+    (const :tag "netsplit: Detect netsplits" netsplit)
+    (const :tag "networks: Provide data about IRC networks" networks)
+    (const :tag "noncommands: Don't display non-IRC commands after evaluation"
           noncommands)
     (const :tag
-          "Notify when the online status of certain users changes"
+          "notify: Notify when the online status of certain users changes"
           notify)
-    (const :tag "Complete nicknames and commands (programmable)"
-          completion)
-    (const :tag "Complete nicknames and commands (old)" hecomplete)
-    (const :tag "Process CTCP PAGE requests from IRC" page)
-    (const :tag "Make displayed lines read-only" readonly)
-    (const :tag "Replace text in messages" replace)
-    (const :tag "Enable an input history" ring)
-    (const :tag "Scroll to the bottom of the buffer" scrolltobottom)
-    (const :tag "Identify to Nickserv (IRC Services) automatically"
+    (const :tag "page: Process CTCP PAGE requests from IRC" page)
+    (const :tag "readonly: Make displayed lines read-only" readonly)
+    (const :tag "replace: Replace text in messages" replace)
+    (const :tag "ring: Enable an input history" ring)
+    (const :tag "scrolltobottom: Scroll to the bottom of the buffer"
+          scrolltobottom)
+    (const :tag "services: Identify to Nickserv (IRC Services) automatically"
           services)
-    (const :tag "Convert smileys to pretty icons" smiley)
-    (const :tag "Play sounds when you receive CTCP SOUND requests"
+    (const :tag "smiley: Convert smileys to pretty icons" smiley)
+    (const :tag "sound: Play sounds when you receive CTCP SOUND requests"
           sound)
-    (const :tag "Add timestamps to messages" stamp)
-    (const :tag "Check spelling" spelling)
-    (const :tag "Track channel activity in the mode-line" track)
-    (const :tag "Truncate buffers to a certain size" truncate)
-    (const :tag "Translate morse code in messages" unmorse)
+    (const :tag "stamp: Add timestamps to messages" stamp)
+    (const :tag "spelling: Check spelling" spelling)
+    (const :tag "track: Track channel activity in the mode-line" track)
+    (const :tag "truncate: Truncate buffers to a certain size" truncate)
+    (const :tag "unmorse: Translate morse code in messages" unmorse)
+    (const :tag "xdcc: Act as an XDCC file-server" xdcc)
     (repeat :tag "Others" :inline t symbol))
   :group 'erc)
 
@@ -1823,6 +1893,8 @@ removed from the list will be disabled."
       (setq req (concat "erc-" (symbol-name mod)))
       (cond
        ;; yuck. perhaps we should bring the filenames into sync?
+       ((string= req "erc-capab-identify")
+       (setq req "erc-capab"))
        ((string= req "erc-completion")
        (setq req "erc-pcomplete"))
        ((string= req "erc-pcomplete")
@@ -1832,9 +1904,10 @@ removed from the list will be disabled."
       (condition-case nil
          (require (intern req))
        (error nil))
-      (funcall (or (intern-soft (concat "erc-" (symbol-name mod) "-mode"))
-                  (error "`%s' is not a known ERC module" mod))
-              1))))
+      (let ((sym (intern-soft (concat "erc-" (symbol-name mod) "-mode"))))
+       (if (fboundp sym)
+           (funcall sym 1)
+         (error "`%s' is not a known ERC module" mod))))))
 
 (defun erc-setup-buffer (buffer)
   "Consults `erc-join-buffer' to find out how to display `BUFFER'."
@@ -1847,31 +1920,31 @@ removed from the list will be disabled."
        ((eq erc-join-buffer 'bury)
         nil)
        ((eq erc-join-buffer 'frame)
-        (funcall '(lambda (frame)
+        (when (or (not erc-reuse-frames)
+                  (not (get-buffer-window buffer t)))
+          ((lambda (frame)
                     (raise-frame frame)
                     (select-frame frame))
                  (make-frame (or erc-frame-alist
                                  default-frame-alist)))
         (switch-to-buffer buffer)
         (when erc-frame-dedicated-flag
-          (set-window-dedicated-p (selected-window) t)))
+          (set-window-dedicated-p (selected-window) t))))
        (t
         (if (active-minibuffer-window)
             (display-buffer buffer)
           (switch-to-buffer buffer)))))
 
-(defun erc (&optional server port nick full-name
-                     connect passwd tgt-list channel process)
-  "ERC is a powerful, modular, and extensible IRC client.
-
-Connect to SERVER on PORT as NICK with FULL-NAME.
+(defun erc-open (&optional server port nick full-name
+                          connect passwd tgt-list channel process)
+  "Connect to SERVER on PORT as NICK with FULL-NAME.
 
 If CONNECT is non-nil, connect to the server.  Otherwise assume
 already connected and just create a separate buffer for the new
 target CHANNEL.
 
 Use PASSWD as user password on the server.  If TGT-LIST is
-non-nil, use it to initialise `erc-default-recipients'.
+non-nil, use it to initialize `erc-default-recipients'.
 
 Returns the buffer for the given server or channel."
   (let ((server-announced-name (when (and (boundp 'erc-session-server)
@@ -1880,9 +1953,12 @@ Returns the buffer for the given server or channel."
        (connected-p (unless connect erc-server-connected))
        (buffer (erc-get-buffer-create server port channel))
        (old-buffer (current-buffer))
+       old-point
        continued-session)
+    (when connect (run-hook-with-args 'erc-before-connect server port nick))
     (erc-update-modules)
     (set-buffer buffer)
+    (setq old-point (point))
     (erc-mode)
     (setq erc-server-announced-name server-announced-name)
     (setq erc-server-connected connected-p)
@@ -1926,10 +2002,6 @@ Returns the buffer for the given server or channel."
     (erc-set-active-buffer buffer)
     ;; last invitation channel
     (setq erc-invitation nil)
-    ;; away flag
-    ;; Should only be used in session-buffers
-    (setq erc-away (let ((serverbuf (erc-server-buffer)))
-                    (and serverbuf (with-current-buffer serverbuf erc-away))))
     ;; Server channel list
     (setq erc-channel-list ())
     ;; login-time 'nick in use' error
@@ -1944,25 +2016,25 @@ Returns the buffer for the given server or channel."
     (setq erc-dbuf
          (when erc-log-p
            (get-buffer-create (concat "*ERC-DEBUG: " server "*"))))
-    (erc-determine-parameters server port nick full-name)
-
-    ;; Saving log file on exit
-    (run-hooks 'erc-connect-pre-hook)
-
-    (when connect
-      (erc-server-connect erc-session-server erc-session-port))
-    (erc-update-mode-line)
-    (set-marker erc-insert-marker (point))
+    ;; set up prompt
     (unless continued-session
       (goto-char (point-max))
       (insert "\n"))
-    (set-marker (process-mark erc-server-process) (point))
     (if continued-session
-       (goto-char (point-max))
+       (goto-char old-point)
       (set-marker erc-insert-marker (point))
       (erc-display-prompt)
       (goto-char (point-max)))
 
+    (erc-determine-parameters server port nick full-name)
+
+    ;; Saving log file on exit
+    (run-hook-with-args 'erc-connect-pre-hook buffer)
+
+    (when connect
+      (erc-server-connect erc-session-server erc-session-port buffer))
+    (erc-update-mode-line)
+
     ;; Now display the buffer in a window as per user wishes.
     (unless (eq buffer old-buffer)
       (when erc-log-p
@@ -1973,11 +2045,13 @@ Returns the buffer for the given server or channel."
 
     buffer))
 
-(defun erc-initialize-log-marker ()
-  "Initialize the `erc-last-saved-position' marker to a sensible position."
+(defun erc-initialize-log-marker (buffer)
+  "Initialize the `erc-last-saved-position' marker to a sensible position.
+BUFFER is the current buffer."
+  (with-current-buffer buffer
     (setq erc-last-saved-position (make-marker))
     (move-marker erc-last-saved-position
-                (1- (marker-position erc-insert-marker))))
+                (1- (marker-position erc-insert-marker)))))
 
 ;; interactive startup
 
@@ -2001,12 +2075,12 @@ If no buffer matches, return nil."
 
 (if (not (fboundp 'read-passwd))
     (defun read-passwd (prompt)
-      "Substitute for read-passwd in early emacsen"
+      "Substitute for `read-passwd' in early emacsen."
       (read-from-minibuffer prompt)))
 
 (defcustom erc-before-connect nil
   "Hook called before connecting to a server.
-This hook gets executed before `erc-select' actually invokes `erc-mode'
+This hook gets executed before `erc' actually invokes `erc-mode'
 with your input data.  The functions in here get called with three
 parameters, SERVER, PORT and NICK."
   :group 'erc-hooks
@@ -2014,7 +2088,7 @@ parameters, SERVER, PORT and NICK."
 
 (defcustom erc-after-connect nil
   "Hook called after connecting to a server.
-This hook gets executed when an end of MOTD has been received. All
+This hook gets executed when an end of MOTD has been received.  All
 functions in here get called with the parameters SERVER and NICK."
   :group 'erc-hooks
   :type 'hook)
@@ -2072,13 +2146,17 @@ functions in here get called with the parameters SERVER and NICK."
     (list :server server :port port :nick nick :password passwd)))
 
 ;;;###autoload
-(defun* erc-select (&key (server (erc-compute-server))
-                        (port   (erc-compute-port))
-                        (nick   (erc-compute-nick))
-                        password
-                        (full-name (erc-compute-full-name)))
-  "Select connection parameters and run ERC.
-Non-interactively, it takes keyword arguments
+(defun* erc (&key (server (erc-compute-server))
+                 (port   (erc-compute-port))
+                 (nick   (erc-compute-nick))
+                 password
+                 (full-name (erc-compute-full-name)))
+  "ERC is a powerful, modular, and extensible IRC client.
+This function is the main entry point for ERC.
+
+It permits you to select connection parameters, and then starts ERC.
+
+Non-interactively, it takes the keyword arguments
    (server (erc-compute-server))
    (port   (erc-compute-port))
    (nick   (erc-compute-nick))
@@ -2087,38 +2165,76 @@ Non-interactively, it takes keyword arguments
 
 That is, if called with
 
-   (erc-select :server \"irc.freenode.net\" :full-name \"Harry S Truman\")
+   (erc :server \"irc.freenode.net\" :full-name \"Harry S Truman\")
 
-server and full-name will be set to those values, whereas
+then the server and full-name will be set to those values, whereas
 `erc-compute-port', `erc-compute-nick' and `erc-compute-full-name' will
 be invoked for the values of the other parameters."
   (interactive (erc-select-read-args))
+  (erc-open server port nick full-name t password))
 
-  (run-hook-with-args 'erc-before-connect server port nick)
-  (erc server port nick erc-user-full-name t password))
-
+;;;###autoload
+(defalias 'erc-select 'erc)
 
-(defun erc-select-ssl (&rest r)
+(defun erc-ssl (&rest r)
   "Interactively select SSL connection parameters and run ERC.
-Arguments are as to erc-select."
+Arguments are the same as for `erc'."
   (interactive (erc-select-read-args))
   (let ((erc-server-connect-function 'erc-open-ssl-stream))
-    (apply 'erc-select r)))
+    (apply 'erc r)))
+
+(defalias 'erc-select-ssl 'erc-ssl)
+
+(declare-function open-ssl-stream "ext:ssl" (name buffer host service))
 
 (defun erc-open-ssl-stream (name buffer host port)
   "Open an SSL stream to an IRC server.
 The process will be given the name NAME, its target buffer will be
-BUFFER.         HOST and PORT specify the connection target."
-  (when (require 'tls)
-    (let ((proc (open-tls-stream name buffer host port)))
+BUFFER.  HOST and PORT specify the connection target."
+  (when (condition-case nil
+           (require 'ssl)
+         (error (message "You don't have ssl.el.  %s"
+                         "Try using `erc-tls' instead.")
+                nil))
+    (let ((proc (open-ssl-stream name buffer host port)))
       ;; Ugly hack, but it works for now. Problem is it is
       ;; very hard to detect when ssl is established, because s_client
       ;; doesn't give any CONNECTIONESTABLISHED kind of message, and
       ;; most IRC servers send nothing and wait for you to identify.
-      ;; Disabled when switching to tls.el -- jas
-      ;(sit-for 5)
+      (sit-for 5)
       proc)))
 
+(defun erc-tls (&rest r)
+  "Interactively select TLS connection parameters and run ERC.
+Arguments are the same as for `erc'."
+  (interactive (erc-select-read-args))
+  (let ((erc-server-connect-function 'erc-open-tls-stream))
+    (apply 'erc r)))
+
+(declare-function open-tls-stream "tls" (name buffer host port))
+
+(defun erc-open-tls-stream (name buffer host port)
+  "Open an TLS stream to an IRC server.
+The process will be given the name NAME, its target buffer will be
+BUFFER.  HOST and PORT specify the connection target."
+  (when (condition-case nil
+           (require 'tls)
+         (error (message "You don't have tls.el.  %s"
+                         "Try using `erc-ssl' instead.")
+                nil))
+    (open-tls-stream name buffer host port)))
+
+;;; Displaying error messages
+
+(defun erc-error (&rest args)
+  "Pass ARGS to `format', and display the result as an error message.
+If `debug-on-error' is set to non-nil, then throw a real error with this
+message instead, to make debugging easier."
+  (if debug-on-error
+      (apply #'error args)
+    (apply #'message args)
+    (beep)))
+
 ;;; Debugging the protocol
 
 (defvar erc-debug-irc-protocol nil
@@ -2126,13 +2242,15 @@ BUFFER.  HOST and PORT specify the connection target."
 
 The buffer is created if it doesn't exist.
 
-NOTE: If this variable is non-nil, and you kill the the only
+NOTE: If this variable is non-nil, and you kill the only
 visible \"*erc-protocol*\" buffer, it will be recreated shortly,
 but you won't see it.
 
 WARNING: Do not set this variable directly!  Instead, use the
 function `erc-toggle-debug-irc-protocol' to toggle its value.")
 
+(declare-function erc-network-name "erc-networks" ())
+
 (defun erc-log-irc-protocol (string &optional outbound)
   "Append STRING to the buffer *erc-protocol*.
 
@@ -2141,7 +2259,7 @@ This only has any effect if `erc-debug-irc-protocol' is non-nil.
 The buffer is created if it doesn't exist.
 
 If OUTBOUND is non-nil, STRING is being sent to the IRC server
-and appears in erc-input-face in the buffer."
+and appears in face `erc-input-face' in the buffer."
   (when erc-debug-irc-protocol
     (let ((network-name (or (ignore-errors (erc-network-name))
                            "???")))
@@ -2182,7 +2300,7 @@ If ARG is non-nil, show the *erc-protocol* buffer."
   (interactive "P")
   (let* ((buf (get-buffer-create "*erc-protocol*")))
     (with-current-buffer buf
-      (erc-view-mode-enter 1)
+      (erc-view-mode-enter)
       (when (null (current-local-map))
        (let ((inhibit-read-only t))
          (insert (erc-make-notice "This buffer displays all IRC protocol traffic exchanged with each server.\n"))
@@ -2223,9 +2341,18 @@ If ARG is non-nil, show the *erc-protocol* buffer."
 I.e. any char in it has the `invisible' property set."
   (text-property-any 0 (length string) 'invisible t string))
 
+(defcustom erc-remove-parsed-property t
+  "Whether to remove the erc-parsed text property after displaying a message.
+
+The default is to remove it, since it causes ERC to take up extra
+memory.  If you have code that relies on this property, then set
+this option to nil."
+  :type 'boolean
+  :group 'erc)
+
 (defun erc-display-line-1 (string buffer)
   "Display STRING in `erc-mode' BUFFER.
-Auxiliary function used in `erc-display-line'. The line gets filtered to
+Auxiliary function used in `erc-display-line'.  The line gets filtered to
 interpret the control characters.  Then, `erc-insert-pre-hook' gets called.
 If `erc-insert-this' is still t, STRING gets inserted into the buffer.
 Afterwards, `erc-insert-modify' and `erc-insert-post-hook' get called.
@@ -2241,8 +2368,8 @@ If STRING is nil, the function does nothing."
          (unless (string-match "\n$" string)
            (setq string (concat string "\n"))
            (when (erc-string-invisible-p string)
-             (erc-put-text-properties 0 (length string) string
-                                      '(invisible intangible))))
+             (erc-put-text-properties 0 (length string)
+                                      '(invisible intangible) string)))
          (erc-log (concat "erc-display-line: " string
                           (format "(%S)" string) " in buffer "
                           (format "%s" buffer)))
@@ -2263,7 +2390,10 @@ If STRING is nil, the function does nothing."
                (save-restriction
                  (narrow-to-region insert-position (point))
                  (run-hooks 'erc-insert-modify-hook)
-                 (run-hooks 'erc-insert-post-hook))))))
+                 (run-hooks 'erc-insert-post-hook)
+                 (when erc-remove-parsed-property
+                   (remove-text-properties (point-min) (point-max)
+                                           '(erc-parsed nil))))))))
        (erc-update-undo-list (- (or (marker-position erc-insert-marker)
                                     (point-max))
                                 insert-position))))))
@@ -2296,7 +2426,7 @@ If STRING is nil, the function does nothing."
        (setq list (cdr list))))))
 
 (defvar erc-valid-nick-regexp "[]a-zA-Z^[;\\`_{}|][]^[;\\`_{}|a-zA-Z0-9-]*"
-  "Regexp which matches all legal characters in a IRC nickname.")
+  "Regexp which matches all valid characters in a IRC nickname.")
 
 (defun erc-is-valid-nick-p (nick)
   "Check if NICK is a valid IRC nickname."
@@ -2309,8 +2439,8 @@ or omitted, the default ERC buffer for the `erc-session-server' is used.
 The BUFFER can be an actual buffer, a list of buffers, 'all or 'active.
 If BUFFER = 'all, the string is displayed in all the ERC buffers for the
 current session.  'active means the current active buffer
-\(`erc-active-buffer').         If the buffer can't be resolved, the current
-buffer is used.         `erc-display-line-1' is used to display STRING.
+\(`erc-active-buffer').  If the buffer can't be resolved, the current
+buffer is used.  `erc-display-line-1' is used to display STRING.
 
 If STRING is nil, the function does nothing."
   (let ((inhibit-point-motion-hooks t)
@@ -2320,9 +2450,8 @@ If STRING is nil, the function does nothing."
                  ((listp buffer) buffer)
                  ((processp buffer) (list (process-buffer buffer)))
                  ((eq 'all buffer)
-                  (and (boundp 'erc-server-process)
-                       ;; Hmm, or all of the same session server?
-                       (erc-buffer-list nil erc-server-process)))
+                  ;; Hmm, or all of the same session server?
+                  (erc-buffer-list nil erc-server-process))
                  ((and (eq 'active buffer) (erc-active-buffer))
                   (list (erc-active-buffer)))
                  ((erc-server-buffer-live-p)
@@ -2337,9 +2466,9 @@ If STRING is nil, the function does nothing."
        (erc-display-line-1 string (current-buffer))))))
 
 (defun erc-display-message-highlight (type string)
-  "Highlight STRING according to TYPE, where erc-TYPE-face is an erc face.
+  "Highlight STRING according to TYPE, where erc-TYPE-face is an ERC face.
 
-See also `erc-make-notice'"
+See also `erc-make-notice'."
   (cond ((eq type 'notice)
         (erc-make-notice string))
        (t
@@ -2362,6 +2491,8 @@ See also `erc-format-message' and `erc-display-line'."
                  msg)))
     (setq string
          (cond
+          ((null type)
+           string)
           ((listp type)
            (mapc (lambda (type)
                    (setq string
@@ -2374,7 +2505,7 @@ See also `erc-format-message' and `erc-display-line'."
     (if (not (erc-response-p parsed))
        (erc-display-line string buffer)
       (unless (member (erc-response.command parsed) erc-hide-list)
-      (erc-put-text-property 0 (length string) 'erc-parsed parsed string)
+       (erc-put-text-property 0 (length string) 'erc-parsed parsed string)
        (erc-put-text-property 0 (length string) 'rear-sticky t string)
        (erc-display-line string buffer)))))
 
@@ -2383,7 +2514,7 @@ See also `erc-format-message' and `erc-display-line'."
 
 This function relies on the erc-parsed text-property being
 present."
-  (let ((prop-val (get-text-property position 'erc-parsed)))
+  (let ((prop-val (erc-get-parsed-vector position)))
     (and prop-val (member (erc-response.command prop-val) list))))
 
 (defvar erc-send-input-line-function 'erc-send-input-line)
@@ -2409,6 +2540,14 @@ See also `erc-server-send'."
        (match-string 1 arglist)
       arglist)))
 
+(defun erc-command-no-process-p (str)
+  "Return non-nil if STR is an ERC command that can be run when the process
+is not alive, nil otherwise."
+  (let ((fun (erc-extract-command-from-line str)))
+    (and fun
+        (symbolp (car fun))
+        (get (car fun) 'process-not-needed))))
+
 (defun erc-command-name (cmd)
   "For CMD being the function name of a ERC command, something like
 erc-cmd-FOO, this returns a string /FOO."
@@ -2423,7 +2562,7 @@ Returns non-nil if the command is actually sent to the server, and nil
 otherwise.
 
 If the command in the LINE is not bound as a function `erc-cmd-<COMMAND>',
-it is passed to `erc-cmd-default'.  If LINE is not a command (ie. doesn't
+it is passed to `erc-cmd-default'.  If LINE is not a command (i.e. doesn't
 start with /<COMMAND>) then it is sent as a message.
 
 An optional FORCE argument forces sending the line when flood
@@ -2518,6 +2657,7 @@ VALUE is computed by evaluating the rest of LINE in Lisp."
 (defalias 'erc-cmd-VAR 'erc-cmd-SET)
 (defalias 'erc-cmd-VARIABLE 'erc-cmd-SET)
 (put 'erc-cmd-SET 'do-not-parse-args t)
+(put 'erc-cmd-SET 'process-not-needed t)
 
 (defun erc-cmd-default (line)
   "Fallback command.
@@ -2533,40 +2673,50 @@ therefore has to contain the command itself as well."
   "Ignore USER.  This should be a regexp matching nick!user@host.
 If no USER argument is specified, list the contents of `erc-ignore-list'."
   (if user
-      (progn
+      (let ((quoted (regexp-quote user)))
+       (when (and (not (string= user quoted))
+                  (y-or-n-p (format "Use regexp-quoted form (%s) instead? "
+                                    quoted)))
+         (setq user quoted))
        (erc-display-line
         (erc-make-notice (format "Now ignoring %s" user))
         'active)
-       (with-current-buffer (erc-server-buffer)
-         (add-to-list 'erc-ignore-list user)))
-    (if (null (with-current-buffer (erc-server-buffer) erc-ignore-list))
+       (erc-with-server-buffer (add-to-list 'erc-ignore-list user)))
+    (if (null (erc-with-server-buffer erc-ignore-list))
        (erc-display-line (erc-make-notice "Ignore list is empty") 'active)
       (erc-display-line (erc-make-notice "Ignore list:") 'active)
       (mapc #'(lambda (item)
                (erc-display-line (erc-make-notice item)
                                  'active))
-           (with-current-buffer (erc-server-buffer) erc-ignore-list))))
+           (erc-with-server-buffer erc-ignore-list))))
   t)
 
 (defun erc-cmd-UNIGNORE (user)
   "Remove the user specified in USER from the ignore list."
-  (let ((ignored-nick (car (with-current-buffer (erc-server-buffer)
-                            (erc-member-ignore-case user erc-ignore-list)))))
-    (if (null ignored-nick)
+  (let ((ignored-nick (car (erc-with-server-buffer
+                            (erc-member-ignore-case (regexp-quote user)
+                                                    erc-ignore-list)))))
+    (unless ignored-nick
+      (if (setq ignored-nick (erc-ignored-user-p user))
+         (unless (y-or-n-p (format "Remove this regexp (%s)? "
+                                   ignored-nick))
+           (setq ignored-nick nil))
        (erc-display-line
         (erc-make-notice (format "%s is not currently ignored!" user))
-        'active)
+        'active)))
+    (when ignored-nick
       (erc-display-line
        (erc-make-notice (format "No longer ignoring %s" user))
-       'active))
-    (with-current-buffer (erc-server-buffer)
-      (setq erc-ignore-list (delete ignored-nick erc-ignore-list))))
+       'active)
+      (erc-with-server-buffer
+       (setq erc-ignore-list (delete ignored-nick erc-ignore-list)))))
   t)
 
 (defun erc-cmd-CLEAR ()
   "Clear the window content."
   (recenter 0)
   t)
+(put 'erc-cmd-CLEAR 'process-not-needed t)
 
 (defun erc-cmd-OPS ()
   "Show the ops in the current channel."
@@ -2600,6 +2750,7 @@ If no USER argument is specified, list the contents of `erc-ignore-list'."
       (erc-display-message
        nil 'notice 'active 'country-unknown ?d tld))
   t))
+(put 'erc-cmd-COUNTRY 'process-not-needed t)
 
 (defun erc-cmd-AWAY (line)
   "Mark the user as being away, the reason being indicated by LINE.
@@ -2618,8 +2769,8 @@ If no reason is given, unset away status."
   "Mark the user as being away everywhere, the reason being indicated by LINE."
   ;; on all server buffers.
   (erc-with-all-buffers-of-server nil
-   #'erc-server-buffer-p
-   (erc-cmd-AWAY line)))
+    #'erc-open-server-buffer-p
+    (erc-cmd-AWAY line)))
 (put 'erc-cmd-GAWAY 'do-not-parse-args t)
 
 (defun erc-cmd-CTCP (nick cmd &rest args)
@@ -2639,7 +2790,7 @@ VERSION and so on.  It is called with ARGS."
 
 If FUNC contains a valid function or variable, help about that
 will be displayed.  If FUNC is empty, display an apropos about
-erc commands.  Otherwise, do apropos in the erc namespace
+ERC commands.  Otherwise, do `apropos' in the ERC namespace
 \(\"erc-.*LINE\"\).
 
 Examples:
@@ -2680,6 +2831,7 @@ For a list of user commands (/join /part, ...):
     t))
 
 (defalias 'erc-cmd-H 'erc-cmd-HELP)
+(put 'erc-cmd-HELP 'process-not-needed t)
 
 (defun erc-cmd-JOIN (channel &optional key)
   "Join the channel given in CHANNEL, optionally with KEY.
@@ -2765,9 +2917,9 @@ LINE has the format: \"#CHANNEL NICK REASON\" or \"NICK REASON\"."
 
 (defun erc-cmd-LOAD (line)
   "Load the script provided in the LINE.
-If LINE continues beyond the file name,
-the rest of it is put in a (local) variable
-`erc-script-args', which can be used in elisp scripts.
+If LINE continues beyond the file name, the rest of
+it is put in a (local) variable `erc-script-args',
+which can be used in Emacs Lisp scripts.
 
 The optional FORCE argument is ignored here - you can't force loading
 a script after exceeding the flood threshold."
@@ -2811,10 +2963,9 @@ If SERVER is non-nil, use that, rather than the current server."
 
 (defun erc-cmd-IDLE (nick)
   "Show the length of time NICK has been idle."
-  (let ((serverbuf (erc-server-buffer))
-       (origbuf (current-buffer))
+  (let ((origbuf (current-buffer))
        symlist)
-    (with-current-buffer serverbuf
+    (erc-with-server-buffer
       (add-to-list 'symlist
                   (cons (erc-once-with-server-event
                          311 `(string= ,nick
@@ -2904,6 +3055,11 @@ LINE has the format \"USER ACTION\"."
    (t nil)))
 (put 'erc-cmd-ME 'do-not-parse-args t)
 
+(defun erc-cmd-ME\'S (line)
+  "Do a /ME command, but add the string \" 's\" to the beginning."
+  (erc-cmd-ME (concat " 's" line)))
+(put 'erc-cmd-ME\'S 'do-not-parse-args t)
+
 (defun erc-cmd-LASTLOG (line)
   "Show all lines in the current buffer matching the regexp LINE.
 
@@ -2918,6 +3074,7 @@ the matching is case-sensitive."
   (occur line)
   t)
 (put 'erc-cmd-LASTLOG 'do-not-parse-args t)
+(put 'erc-cmd-LASTLOG 'process-not-needed t)
 
 (defun erc-send-message (line &optional force)
   "Send LINE to the current channel or user and display it.
@@ -2972,8 +3129,8 @@ The rest of LINE is the message to send."
 (defun erc-cmd-NICK (nick)
   "Change current nickname to NICK."
   (erc-log (format "cmd: NICK: %s (erc-bad-nick: %S)" nick erc-bad-nick))
-  (let ((nicklen (cdr (assoc "NICKLEN" (with-current-buffer (erc-server-buffer)
-                                   erc-server-parameters)))))
+  (let ((nicklen (cdr (assoc "NICKLEN" (erc-with-server-buffer
+                                        erc-server-parameters)))))
     (and nicklen (> (length nick) (string-to-number nicklen))
         (erc-display-message
          nil 'notice 'active 'nick-too-long
@@ -3033,14 +3190,35 @@ just as you provided it.  Use this command with care!"
    (t nil)))
 (put 'erc-cmd-QUOTE 'do-not-parse-args t)
 
+(defcustom erc-query-display 'window
+  "Indicates how to display query buffers when using the /QUERY
+command to talk to someone.
+
+The default behavior is to display the message in a new window
+and bring it to the front.  See the documentation for
+`erc-join-buffer' for a description of the available choices.
+
+See also `erc-auto-query' to decide how private messages from
+other people should be displayed."
+  :group 'erc-query
+  :type '(choice (const :tag "Split window and select" window)
+                (const :tag "Split window, don't select" window-noselect)
+                (const :tag "New frame" frame)
+                (const :tag "Bury in new buffer" bury)
+                (const :tag "Use current buffer" buffer)
+                (const :tag "Use current buffer" t)))
+
 (defun erc-cmd-QUERY (&optional user)
   "Open a query with USER.
 The type of query window/frame/etc will depend on the value of
-`erc-join-buffer'.  If USER is omitted, close the current query buffer if one
-exists - except this is broken now ;-)"
+`erc-query-display'.
+
+If USER is omitted, close the current query buffer if one exists
+- except this is broken now ;-)"
   (interactive
    (list (read-from-minibuffer "Start a query with: " nil)))
-  (let ((session-buffer (erc-server-buffer)))
+  (let ((session-buffer (erc-server-buffer))
+       (erc-join-buffer erc-query-display))
     (if user
        (erc-query user session-buffer)
       ;; currently broken, evil hack to display help anyway
@@ -3112,7 +3290,8 @@ the message given by REASON."
    ((string-match "^\\s-*\\(.*\\)$" reason)
     (let* ((s (match-string 1 reason))
           (buffer (erc-server-buffer))
-          (reason (funcall erc-quit-reason (if (equal s "") nil s))))
+          (reason (funcall erc-quit-reason (if (equal s "") nil s)))
+          server-proc)
       (with-current-buffer (if (and buffer
                                    (bufferp buffer))
                               buffer
@@ -3120,10 +3299,18 @@ the message given by REASON."
        (erc-log (format "cmd: QUIT: %s" reason))
        (setq erc-server-quitting t)
        (erc-set-active-buffer (erc-server-buffer))
+       (setq server-proc erc-server-process)
        (erc-server-send (format "QUIT :%s" reason)))
-      (run-hook-with-args 'erc-quit-hook erc-server-process)
+      (run-hook-with-args 'erc-quit-hook server-proc)
       (when erc-kill-queries-on-quit
-       (erc-kill-query-buffers erc-server-process)))
+       (erc-kill-query-buffers server-proc))
+      ;; if the process has not been killed within 4 seconds, kill it
+      (run-at-time 4 nil
+                  (lambda (proc)
+                    (when (and (processp proc)
+                               (memq (process-status proc) '(run open)))
+                      (delete-process proc)))
+                  server-proc))
     t)
    (t nil)))
 
@@ -3131,30 +3318,57 @@ the message given by REASON."
 (defalias 'erc-cmd-EXIT 'erc-cmd-QUIT)
 (defalias 'erc-cmd-SIGNOFF 'erc-cmd-QUIT)
 (put 'erc-cmd-QUIT 'do-not-parse-args t)
+(put 'erc-cmd-QUIT 'process-not-needed t)
 
 (defun erc-cmd-GQUIT (reason)
   "Disconnect from all servers at once with the same quit REASON."
-  (erc-with-all-buffers-of-server nil #'(lambda ()
-                                         (and (erc-server-buffer-p)
-                                              (erc-server-process-alive)))
-                                 (erc-cmd-QUIT reason)))
+  (erc-with-all-buffers-of-server nil #'erc-open-server-buffer-p
+                                 (erc-cmd-QUIT reason))
+  (when erc-kill-queries-on-quit
+    ;; if the query buffers have not been killed within 4 seconds,
+    ;; kill them
+    (run-at-time
+     4 nil
+     (lambda ()
+       (dolist (buffer (erc-buffer-list (lambda (buf)
+                                         (not (erc-server-buffer-p buf)))))
+        (kill-buffer buffer)))))
+  t)
 
 (defalias 'erc-cmd-GQ 'erc-cmd-GQUIT)
 (put 'erc-cmd-GQUIT 'do-not-parse-args t)
+(put 'erc-cmd-GQUIT 'process-not-needed t)
+
+(defun erc-cmd-RECONNECT ()
+  "Try to reconnect to the current IRC server."
+  (let ((buffer (erc-server-buffer))
+       (process nil))
+    (unless (buffer-live-p buffer)
+      (setq buffer (current-buffer)))
+    (with-current-buffer buffer
+      (setq erc-server-quitting nil)
+      (setq erc-server-reconnecting t)
+      (setq erc-server-reconnect-count 0)
+      (setq process (get-buffer-process (erc-server-buffer)))
+      (if process
+         (delete-process process)
+       (erc-server-reconnect))
+      (setq erc-server-reconnecting nil)))
+  t)
+(put 'erc-cmd-RECONNECT 'process-not-needed t)
 
 (defun erc-cmd-SERVER (server)
   "Connect to SERVER, leaving existing connection intact."
   (erc-log (format "cmd: SERVER: %s" server))
   (condition-case nil
-      (erc-select :server server :nick (erc-current-nick))
+      (erc :server server :nick (erc-current-nick))
     (error
-     (message "Cannot find host %s." server)
-     (beep)))
+     (erc-error "Cannot find host %s." server)))
   t)
+(put 'erc-cmd-SERVER 'process-not-needed t)
 
-(eval-when-compile
-  (defvar motif-version-string)
-  (defvar gtk-version-string))
+(defvar motif-version-string)
+(defvar gtk-version-string)
 
 (defun erc-cmd-SV ()
   "Say the current ERC and Emacs version into channel."
@@ -3170,7 +3384,6 @@ the message given by REASON."
                                   ((featurep 'gtk)
                                    (concat ", GTK+ Version "
                                            gtk-version-string))
-                                  ((featurep 'mac-carbon) ", Mac Carbon")
                                   ((featurep 'x-toolkit) ", X toolkit")
                                   (t ""))
                             (if (and (boundp 'x-toolkit-scroll-bars)
@@ -3312,7 +3525,7 @@ The ban list is fetched from the server if necessary."
        (setq erc-server-367-functions 'erc-banlist-store
              erc-channel-banlist nil)
        ;; fetch the ban list then callback
-       (with-current-buffer (erc-server-buffer)
+       (erc-with-server-buffer
          (erc-once-with-server-event
           368
           `(with-current-buffer ,chnl-name
@@ -3334,7 +3547,7 @@ The ban list is fetched from the server if necessary."
                                  (and (boundp 'fill-column)
                                       fill-column)
                                  (1- (window-width))))
-            (separator (make-string erc-fill-column (string-to-char "=")))
+            (separator (make-string erc-fill-column ?=))
             (fmt (concat
                   "%-" (number-to-string (/ erc-fill-column 2)) "s"
                   "%" (number-to-string (/ erc-fill-column 2)) "s")))
@@ -3382,7 +3595,7 @@ Unban all currently banned users in the current channel."
       (let ((old-367-hook erc-server-367-functions))
        (setq erc-server-367-functions 'erc-banlist-store)
       ;; fetch the ban list then callback
-      (with-current-buffer (erc-server-buffer)
+      (erc-with-server-buffer
        (erc-once-with-server-event
         368
         `(with-current-buffer ,chnl
@@ -3400,7 +3613,7 @@ Unban all currently banned users in the current channel."
        (lambda (x)
         (erc-server-send
          (format "MODE %s -%s %s" (erc-default-target)
-                 (make-string (length x) (string-to-char "b"))
+                 (make-string (length x) ?b)
                        (mapconcat 'identity x " "))))
        (erc-group-list bans 3))))
        t))))
@@ -3495,7 +3708,9 @@ If FACE is non-nil, it will be used to propertize the prompt.  If it is nil,
   (let ((minibuffer-allow-text-properties t)
        (read-map minibuffer-local-map))
     (insert (read-from-minibuffer "Message: "
-                                 (string last-command-char) read-map))
+                                 (string (if (featurep 'xemacs)
+                                             last-command-char
+                                           last-command-event)) read-map))
     (erc-send-current-line)))
 
 (defvar erc-action-history-list ()
@@ -3521,7 +3736,7 @@ If `point' is at the beginning of a channel name, use that as default."
                   (set-buffer (process-buffer erc-server-process))
                   erc-channel-list)))
       (completing-read "Join channel: " table nil nil nil nil chnl))
-    (when erc-prompt-for-channel-key
+    (when (or current-prefix-arg erc-prompt-for-channel-key)
       (read-from-minibuffer "Channel key (RET for none): " nil))))
   (erc-cmd-JOIN channel (when (>= (length key) 1) key)))
 
@@ -3531,9 +3746,8 @@ If `point' is at the beginning of a channel name, use that as default."
    (list
     (if (and (boundp 'reason) (stringp reason) (not (string= reason "")))
        reason
-      (read-from-minibuffer (concat "Leave " (erc-default-target)
-                                   ", Reason? ")
-                           (cons "No reason" 0)))))
+      (read-from-minibuffer (concat "Reason for leaving " (erc-default-target)
+                                   ": ")))))
   (erc-cmd-PART (concat (erc-default-target)" " reason)))
 
 (defun erc-set-topic (topic)
@@ -3543,8 +3757,9 @@ If `point' is at the beginning of a channel name, use that as default."
     (read-from-minibuffer
      (concat "Set topic of " (erc-default-target) ": ")
      (when erc-channel-topic
-       (cons (apply 'concat (butlast (split-string erc-channel-topic "\C-o")))
-            0)))))
+       (let ((ss (split-string erc-channel-topic "\C-o")))
+        (cons (apply 'concat (if (cdr ss) (butlast ss) ss))
+              0))))))
   (let ((topic-list (split-string topic "\C-o"))) ; strip off the topic setter
     (erc-cmd-TOPIC (concat (erc-default-target) " " (car topic-list)))))
 
@@ -3663,34 +3878,36 @@ To change how this query window is displayed, use `let' to bind
               (buffer-live-p server)
               (set-buffer server))
     (error "Couldn't switch to server buffer"))
-  (let ((buf (erc erc-session-server
-                 erc-session-port
-                 (erc-current-nick)
-                 erc-session-user-full-name
-                 nil
-                 nil
-                 (list target)
-                 target
-                 erc-server-process)))
+  (let ((buf (erc-open erc-session-server
+                      erc-session-port
+                      (erc-current-nick)
+                      erc-session-user-full-name
+                      nil
+                      nil
+                      (list target)
+                      target
+                      erc-server-process)))
     (unless buf
       (error "Couldn't open query window"))
     (erc-update-mode-line)
     buf))
 
-(defcustom erc-auto-query nil
+(defcustom erc-auto-query 'window-noselect
   "If non-nil, create a query buffer each time you receive a private message.
+If the buffer doesn't already exist, it is created.
 
-If the buffer doesn't already exist it is created.  This can be
-set to a symbol, to control how the new query window should
-appear.  See the documentation for `erc-join-buffer' for
-available choices."
+This can be set to a symbol, to control how the new query window
+should appear.  The default behavior is to display the buffer in
+a new window, but not to select it.  See the documentation for
+`erc-join-buffer' for a description of the available choices."
   :group 'erc-query
-  :type '(choice (const nil)
-                (const buffer)
-                (const window)
-                (const window-noselect)
-                (const bury)
-                (const frame)))
+  :type '(choice (const :tag "Don't create query window" nil)
+                (const :tag "Split window and select" window)
+                (const :tag "Split window, don't select" window-noselect)
+                (const :tag "New frame" frame)
+                (const :tag "Bury in new buffer" bury)
+                (const :tag "Use current buffer" buffer)
+                (const :tag "Use current buffer" t)))
 
 (defcustom erc-query-on-unjoined-chan-privmsg t
   "If non-nil create query buffer on receiving any PRIVMSG at all.
@@ -3751,7 +3968,7 @@ See also `erc-display-error-notice'."
     (setq erc-nick-change-attempt-count (+ erc-nick-change-attempt-count 1))
     (let ((newnick (nth 1 erc-default-nicks))
          (nicklen (cdr (assoc "NICKLEN"
-                              (with-current-buffer (erc-server-buffer)
+                              (erc-with-server-buffer
                                 erc-server-parameters)))))
       (setq erc-bad-nick t)
       ;; try to use a different nick
@@ -3779,7 +3996,7 @@ See also `erc-display-error-notice'."
 
 (defgroup erc-server-hooks nil
   "Server event callbacks.
-Every server event - like numeric replies - has it's own hook.
+Every server event - like numeric replies - has its own hook.
 Those hooks are all called using `run-hook-with-args-until-success'.
 They receive as first argument the process object from where the event
 originated from,
@@ -3809,7 +4026,7 @@ and as second argument the event parsed as a vector."
               (string= target (erc-current-nick)))
           (not (erc-get-buffer query proc))
           (not (erc-is-message-ctcp-and-not-action-p msg))
-          (let ((erc-join-buffer erc-auto-query))
+          (let ((erc-query-display erc-auto-query))
             (erc-cmd-QUERY query))
           nil))))
 
@@ -3843,32 +4060,29 @@ and as second argument the event parsed as a vector."
   :type 'function)
 
 (defun erc-format-nick (&optional user channel-data)
-  "Standard nickname formatting function.  Only returns the value
-of NICK."
-  (if user
-      (erc-server-user-nickname user)))
+  "Return the nickname of USER.
+See also `erc-format-nick-function'."
+  (when user (erc-server-user-nickname user)))
 
 (defun erc-format-@nick (&optional user channel-data)
-  "Format a nickname such that @ or + are prefix for the NICK
-if OP or VOICE are t respectively."
-  (if user
-      (let (op voice)
-       (if channel-data
-           (setq op (erc-channel-user-op channel-data)
-                 voice (erc-channel-user-voice channel-data)))
-    (concat (if voice "+" "")
-           (if op "@" "")
-               (erc-server-user-nickname user)))))
+  "Format the nickname of USER showing if USER is an operator or has voice.
+Operators have \"@\" and users with voice have \"+\" as a prefix.
+Use CHANNEL-DATA to determine op and voice status.
+See also `erc-format-nick-function'."
+  (when user
+    (let ((op (and channel-data (erc-channel-user-op channel-data) "@"))
+         (voice (and channel-data (erc-channel-user-voice channel-data) "+")))
+      (concat voice op (erc-server-user-nickname user)))))
 
 (defun erc-format-my-nick ()
-  "Return the beginning of this user's message, correctly propertized"
+  "Return the beginning of this user's message, correctly propertized."
   (if erc-show-my-nick
       (let ((open "<")
            (close "> ")
            (nick (erc-current-nick)))
        (concat
         (erc-propertize open 'face 'erc-default-face)
-        (erc-propertize nick 'face 'erc-nick-default-face)
+        (erc-propertize nick 'face 'erc-my-nick-face)
         (erc-propertize close 'face 'erc-default-face)))
     (let ((prefix "> "))
       (erc-propertize prefix 'face 'erc-default-face))))
@@ -3885,7 +4099,7 @@ and always returns t."
 (defun erc-echo-notice-in-target-buffer (s parsed buffer sender)
   "Echos a private notice in BUFFER, if BUFFER is non-nil.  This
 function is designed to be added to either `erc-echo-notice-hook'
-or `erc-echo-notice-always-hook', and returns non-nil iff BUFFER
+or `erc-echo-notice-always-hook', and returns non-nil if BUFFER
 is non-nil."
   (if buffer
       (progn (erc-display-message parsed nil buffer s) t)
@@ -3909,7 +4123,7 @@ designed to be added to either `erc-echo-notice-hook' or
   "Echos a private notice in the active buffer if the active
 buffer is not the server buffer.  This function is designed to be
 added to either `erc-echo-notice-hook' or
-`erc-echo-notice-always-hook', and returns non-nil iff the active
+`erc-echo-notice-always-hook', and returns non-nil if the active
 buffer is not the server buffer."
   (if (not (eq (erc-server-buffer) (erc-active-buffer)))
       (progn (erc-display-message parsed nil 'active s) t)
@@ -3926,11 +4140,11 @@ designed to be added to either `erc-echo-notice-hook' or
   "Echos a private notice in all of the buffers for which SENDER
 is a member.  This function is designed to be added to either
 `erc-echo-notice-hook' or `erc-echo-notice-always-hook', and
-returns non-nil iff there is at least one buffer for which the
+returns non-nil if there is at least one buffer for which the
 sender is a member.
 
 See also: `erc-echo-notice-in-first-user-buffer',
-`erc-buffer-list-with-nick'"
+`erc-buffer-list-with-nick'."
   (let ((buffers (erc-buffer-list-with-nick sender erc-server-process)))
     (if buffers
        (progn (erc-display-message parsed nil buffers s) t)
@@ -3940,12 +4154,12 @@ See also: `erc-echo-notice-in-first-user-buffer',
   "Echos a private notice in BUFFER and in all of the buffers for
 which SENDER is a member.  This function is designed to be added
 to either `erc-echo-notice-hook' or
-`erc-echo-notice-always-hook', and returns non-nil iff there is
+`erc-echo-notice-always-hook', and returns non-nil if there is
 at least one buffer for which the sender is a member or the
 default target.
 
 See also: `erc-echo-notice-in-user-buffers',
-`erc-buffer-list-with-nick'"
+`erc-buffer-list-with-nick'."
   (let ((buffers (erc-buffer-list-with-nick sender erc-server-process)))
     (add-to-list 'buffers buffer)
     (if buffers
@@ -3956,11 +4170,11 @@ See also: `erc-echo-notice-in-user-buffers',
   "Echos a private notice in one of the buffers for which SENDER
 is a member.  This function is designed to be added to either
 `erc-echo-notice-hook' or `erc-echo-notice-always-hook', and
-returns non-nil iff there is at least one buffer for which the
+returns non-nil if there is at least one buffer for which the
 sender is a member.
 
 See also: `erc-echo-notice-in-user-buffers',
-`erc-buffer-list-with-nick'"
+`erc-buffer-list-with-nick'."
   (let ((buffers (erc-buffer-list-with-nick sender erc-server-process)))
     (if buffers
        (progn (erc-display-message parsed nil (car buffers) s) t)
@@ -3971,7 +4185,7 @@ See also: `erc-echo-notice-in-user-buffers',
 (defun erc-banlist-store (proc parsed)
   "Record ban entries for a channel."
   (multiple-value-bind (channel mask whoset)
-      (cdr (erc-response.command-args parsed))
+      (values-list (cdr (erc-response.command-args parsed)))
     ;; Determine to which buffer the message corresponds
     (let ((buffer (erc-get-buffer channel proc)))
       (with-current-buffer buffer
@@ -4043,25 +4257,30 @@ See also: `erc-echo-notice-in-user-buffers',
 (defun erc-connection-established (proc parsed)
   "Run just after connection.
 
-Set user modes and run `erc-after-connect hook'."
-  (unless erc-server-connected ; only once per session
-    (let ((server (or erc-server-announced-name (erc-response.sender parsed)))
-         (nick (car (erc-response.command-args parsed ))))
-      (setq erc-server-connected t)
-      (erc-update-mode-line)
-      (erc-set-initial-user-mode nick)
-      (erc-server-setup-periodical-server-ping)
-      (run-hook-with-args 'erc-after-connect server nick))))
-
-(defun erc-set-initial-user-mode (nick)
-  "If `erc-user-mode' is non-nil for NICK, set the user modes."
-  (when erc-user-mode
-    (let ((mode (if (functionp erc-user-mode)
-                   (funcall erc-user-mode)
-                 erc-user-mode)))
-      (when (stringp mode)
-       (erc-log (format "changing mode for %s to %s" nick mode))
-       (erc-server-send (format "MODE %s %s" nick mode))))))
+Set user modes and run `erc-after-connect' hook."
+  (with-current-buffer (process-buffer proc)
+    (unless erc-server-connected ; only once per session
+      (let ((server (or erc-server-announced-name
+                       (erc-response.sender parsed)))
+           (nick (car (erc-response.command-args parsed)))
+           (buffer (process-buffer proc)))
+       (setq erc-server-connected t)
+       (erc-update-mode-line)
+       (erc-set-initial-user-mode nick buffer)
+       (erc-server-setup-periodical-ping buffer)
+       (run-hook-with-args 'erc-after-connect server nick)))))
+
+(defun erc-set-initial-user-mode (nick buffer)
+  "If `erc-user-mode' is non-nil for NICK, set the user modes.
+The server buffer is given by BUFFER."
+  (with-current-buffer buffer
+    (when erc-user-mode
+      (let ((mode (if (functionp erc-user-mode)
+                     (funcall erc-user-mode)
+                   erc-user-mode)))
+       (when (stringp mode)
+         (erc-log (format "changing mode for %s to %s" nick mode))
+         (erc-server-send (format "MODE %s %s" nick mode)))))))
 
 (defun erc-display-error-notice (parsed string)
   "Display STRING as an error notice.
@@ -4273,8 +4492,10 @@ See also `erc-display-message'."
   nil)
 
 (defun erc-process-away (proc away-p)
-  ;; FIXME: This docstring is AWFUL -- Lawrence 2004-01-08
-  "Process the user being away, or returning from an away break."
+  "Toggle the away status of the user depending on the value of AWAY-P.
+
+If nil, set the user as away.
+If non-nil, return from being away."
   (let ((sessionbuf (process-buffer proc)))
     (when sessionbuf
       (with-current-buffer sessionbuf
@@ -4286,14 +4507,12 @@ See also `erc-display-message'."
                          erc-nick)))
        (cond
         (away-p
-         (erc-with-all-buffers-of-server proc nil
-                                         (setq erc-away (current-time))))
+         (setq erc-away (current-time)))
         (t
          (let ((away-time erc-away))
            ;; away must be set to NIL BEFORE sending anything to prevent
            ;; an infinite recursion
-           (erc-with-all-buffers-of-server proc nil
-                                           (setq erc-away nil))
+           (setq erc-away nil)
            (save-excursion
              (set-buffer (erc-active-buffer))
              (when erc-public-away-p
@@ -4333,33 +4552,65 @@ See also `erc-channel-begin-receiving-names'."
           erc-channel-users)
   (setq erc-channel-new-member-names nil))
 
+(defun erc-parse-prefix ()
+  "Return an alist of valid prefix character types and their representations.
+Example: (operator) o => @, (voiced) v => +."
+  (let ((str (or (cdr (assoc "PREFIX" (erc-with-server-buffer
+                                       erc-server-parameters)))
+                ;; provide a sane default
+                "(ov)@+"))
+       types chars)
+    (when (string-match "^(\\([^)]+\\))\\(.+\\)$" str)
+      (setq types (match-string 1 str)
+           chars (match-string 2 str))
+      (let ((len (min (length types) (length chars)))
+           (i 0)
+           (alist nil))
+       (while (< i len)
+         (setq alist (cons (cons (elt types i) (elt chars i))
+                           alist))
+         (setq i (1+ i)))
+       alist))))
+
 (defun erc-channel-receive-names (names-string)
   "This function is for internal use only.
 
 Update `erc-channel-users' according to NAMES-STRING.
 NAMES-STRING is a string listing some of the names on the
 channel."
-  (let (names name op voice)
-      ;; We need to delete "" because in XEmacs, (split-string "a ")
-      ;; returns ("a" "").
-      (setq names (delete "" (split-string names-string)))
-      (let ((erc-channel-members-changed-hook nil))
-       (dolist (item names)
-         (cond ((string-match "^@\\(.*\\)$" item)
-                (setq name (match-string 1 item)
-                      op 'on
-                      voice 'off))
-               ((string-match "^+\\(.*\\)$" item)
-                (setq name (match-string 1 item)
-                      op 'off
-                      voice 'on))
-               (t (setq name item
-                        op 'off
-                        voice 'off)))
-       (puthash (erc-downcase name) t
-                erc-channel-new-member-names)
-       (erc-update-current-channel-member
-        name name t op voice)))
+  (let (prefix op-ch voice-ch names name op voice)
+    (setq prefix (erc-parse-prefix))
+    (setq op-ch (cdr (assq ?o prefix))
+         voice-ch (cdr (assq ?v prefix)))
+    ;; We need to delete "" because in XEmacs, (split-string "a ")
+    ;; returns ("a" "").
+    (setq names (delete "" (split-string names-string)))
+    (let ((erc-channel-members-changed-hook nil))
+      (dolist (item names)
+       (let ((updatep t)
+             ch)
+         (if (rassq (elt item 0) prefix)
+             (cond ((= (length item) 1)
+                    (setq updatep nil))
+                   ((eq (elt item 0) op-ch)
+                    (setq name (substring item 1)
+                          op 'on
+                          voice 'off))
+                   ((eq (elt item 0) voice-ch)
+                    (setq name (substring item 1)
+                          op 'off
+                          voice 'on))
+                   (t (setq name (substring item 1)
+                            op 'off
+                            voice 'off)))
+           (setq name item
+                 op 'off
+                 voice 'off))
+         (when updatep
+           (puthash (erc-downcase name) t
+                    erc-channel-new-member-names)
+           (erc-update-current-channel-member
+            name name t op voice)))))
     (run-hooks 'erc-channel-members-changed-hook)))
 
 (defcustom erc-channel-members-changed-hook nil
@@ -4373,7 +4624,7 @@ The buffer where the change happened is current while this hook is called."
   "Updates the stored user information for the user with nickname
 NICK.
 
-See also: `erc-update-user'"
+See also: `erc-update-user'."
   (erc-update-user (erc-get-server-user nick) new-nick
                   host login full-name info))
 
@@ -4384,9 +4635,9 @@ struct.  Any of NEW-NICK, HOST, LOGIN, FULL-NAME, INFO which are
 non-nil and not equal to the existing values for USER are used to
 replace the stored values in USER.
 
-If, any only if a change is made,
+If, and only if, a change is made,
 `erc-channel-members-changed-hook' is run for each channel for
-which USER is a member, and `t' is returned."
+which USER is a member, and t is returned."
   (let (changed)
     (when user
       (when (and new-nick
@@ -4423,10 +4674,10 @@ which USER is a member, and `t' is returned."
        update-message-time)
   "Updates the stored user information for the user with nickname
 NICK.  `erc-update-user' is called to handle changes to nickname,
-host, login, full-name, and info.  If `op' or `voice' are
-non-nil, they must be equal to either `on' or `off', in which
-case the operator or voice status of USER in the current channel
-is changed accordingly.  If `update-message-time' is non-nil, the
+HOST, LOGIN, FULL-NAME, and INFO.  If OP or VOICE are non-nil,
+they must be equal to either `on' or `off', in which case the
+operator or voice status of the user in the current channel is
+changed accordingly.  If UPDATE-MESSAGE-TIME is non-nil, the
 last-message-time of the user in the current channel is set
 to (current-time).
 
@@ -4435,7 +4686,7 @@ information if it is not already present in the user or channel
 lists.
 
 If, and only if, changes are made, or the user is added,
-`erc-channel-members-updated-hook' is run, and `t' is returned.
+`erc-channel-members-updated-hook' is run, and t is returned.
 
 See also: `erc-update-user' and `erc-update-channel-member'."
   (let* (changed user-changed
@@ -4451,15 +4702,15 @@ See also: `erc-update-user' and `erc-update-channel-member'."
              (setq changed t)
            (setf (erc-channel-user-op cuser)
                  (cond ((eq op 'on) t)
-                                  ((eq op 'off) nil)
-                                  (t op))))
+                       ((eq op 'off) nil)
+                       (t op))))
          (when (and voice
                     (not (eq (erc-channel-user-voice cuser) voice)))
              (setq changed t)
            (setf (erc-channel-user-voice cuser)
                  (cond ((eq voice 'on) t)
-                                     ((eq voice 'off) nil)
-                                     (t voice))))
+                       ((eq voice 'off) nil)
+                       (t voice))))
          (when update-message-time
            (setf (erc-channel-user-last-message-time cuser) (current-time)))
          (setq user-changed
@@ -4481,11 +4732,11 @@ See also: `erc-update-user' and `erc-update-channel-member'."
                      (erc-server-user-buffers user))))
        (setq cuser (make-erc-channel-user
                     :op (cond ((eq op 'on) t)
-                                      ((eq op 'off) nil)
-                                      (t op))
+                              ((eq op 'off) nil)
+                              (t op))
                     :voice (cond ((eq voice 'on) t)
-                                      ((eq voice 'off) nil)
-                                      (t voice))
+                                 ((eq voice 'off) nil)
+                                 (t voice))
                     :last-message-time
                     (if update-message-time (current-time))))
        (puthash (erc-downcase nick) (cons user cuser)
@@ -4501,7 +4752,7 @@ See also: `erc-update-user' and `erc-update-channel-member'."
   "Updates user and channel information for the user with
 nickname NICK in channel CHANNEL.
 
-See also: `erc-update-current-channel-member'"
+See also: `erc-update-current-channel-member'."
   (erc-with-buffer
    (channel)
    (erc-update-current-channel-member nick new-nick add op voice host
@@ -4509,8 +4760,8 @@ See also: `erc-update-current-channel-member'"
                                      update-message-time)))
 
 (defun erc-remove-current-channel-member (nick)
-  "Remove NICK from current channel membership list.  Runs
-`erc-channel-members-changed-hook'."
+  "Remove NICK from current channel membership list.
+Runs `erc-channel-members-changed-hook'."
   (let ((channel-data (erc-get-channel-user nick)))
     (when channel-data
       (erc-remove-channel-user nick)
@@ -4545,7 +4796,7 @@ TOPIC string to the current topic."
         ;; list of triples: (mode-char 'on/'off argument)
         (arg-modes (nth 2 modes)))
     (cond ((erc-channel-p tgt); channel modes
-          (let ((buf (and (boundp 'erc-server-process) erc-server-process
+          (let ((buf (and erc-server-process
                           (erc-get-buffer tgt erc-server-process))))
             (when buf
               (with-current-buffer buf
@@ -4637,7 +4888,7 @@ person who changed the modes."
         (arg-modes (nth 2 modes)))
     ;; now parse the modes changes and do the updates
     (cond ((erc-channel-p tgt); channel modes
-          (let ((buf (and (boundp 'erc-server-process) erc-server-process
+          (let ((buf (and erc-server-process
                           (erc-get-buffer tgt erc-server-process))))
             (when buf
               ;; FIXME! This used to have an original buffer
@@ -4715,7 +4966,7 @@ So far the following TYPE/L pairs are supported:
 
 (defun erc-highlight-notice (s)
   "Highlight notice message S and return it.
-See also variable `erc-notice-highlight-type'"
+See also variable `erc-notice-highlight-type'."
   (cond
    ((eq erc-notice-highlight-type 'prefix)
     (erc-put-text-property 0 (length erc-notice-prefix)
@@ -4790,9 +5041,9 @@ each property to the corresponding value in VALUE-LIST."
     (setq value-list (mapcar (lambda (x)
                               t)
                             properties)))
-  (mapcar* (lambda (prop value)
-            (erc-put-text-property start end prop value object))
-          properties value-list))
+  (while (and properties value-list)
+    (erc-put-text-property
+     start end (pop properties) (pop value-list) object)))
 
 ;;; Input area handling:
 
@@ -4814,50 +5065,51 @@ Specifically, return the position of `erc-insert-marker'."
   (interactive)
   (save-restriction
     (widen)
-    (cond
-     ((< (point) (erc-beg-of-input-line))
-      (message "Point is not in the input area")
-      (beep))
-     ((not (erc-server-buffer-live-p))
-      (message "ERC: No process running")
-      (beep))
-     (t
-      (erc-set-active-buffer (current-buffer))
+    (if (< (point) (erc-beg-of-input-line))
+       (erc-error "Point is not in the input area")
       (let ((inhibit-read-only t)
            (str (erc-user-input))
            (old-buf (current-buffer)))
-
-       ;; Kill the input and the prompt
-       (delete-region (erc-beg-of-input-line)
-                      (erc-end-of-input-line))
-
-       (unwind-protect
-           (erc-send-input str)
-         ;; Fix the buffer if the command didn't kill it
-         (when (buffer-live-p old-buf)
-           (with-current-buffer old-buf
-             (save-restriction
-               (widen)
-               (goto-char (point-max))
-               (set-marker (process-mark erc-server-process) (point))
-               (set-marker erc-insert-marker (point))
-               (let ((buffer-modified (buffer-modified-p)))
-                 (erc-display-prompt)
-                 (set-buffer-modified-p buffer-modified))))))
-
-       ;; Only when last hook has been run...
-       (run-hook-with-args 'erc-send-completed-hook str))))))
+       (if (and (not (erc-server-buffer-live-p))
+                (not (erc-command-no-process-p str)))
+           (erc-error "ERC: No process running")
+         (erc-set-active-buffer (current-buffer))
+
+         ;; Kill the input and the prompt
+         (delete-region (erc-beg-of-input-line)
+                        (erc-end-of-input-line))
+
+         (unwind-protect
+             (erc-send-input str)
+           ;; Fix the buffer if the command didn't kill it
+           (when (buffer-live-p old-buf)
+             (with-current-buffer old-buf
+               (save-restriction
+                 (widen)
+                 (goto-char (point-max))
+                 (when (processp erc-server-process)
+                   (set-marker (process-mark erc-server-process) (point)))
+                 (set-marker erc-insert-marker (point))
+                 (let ((buffer-modified (buffer-modified-p)))
+                   (erc-display-prompt)
+                   (set-buffer-modified-p buffer-modified))))))
+
+         ;; Only when last hook has been run...
+         (run-hook-with-args 'erc-send-completed-hook str))))))
 
 (defun erc-user-input ()
   "Return the input of the user in the current buffer."
-  (buffer-substring
+  (buffer-substring-no-properties
    erc-input-marker
    (erc-end-of-input-line)))
 
+(defvar erc-command-regexp "^/\\([A-Za-z']+\\)\\(\\s-+.*\\|\\s-*\\)$"
+  "Regular expression used for matching commands in ERC.")
+
 (defun erc-send-input (input)
   "Treat INPUT as typed in by the user. It is assumed that the input
 and the prompt is already deleted.
-This returns non-nil only iff we actually send anything."
+This returns non-nil only if we actually send anything."
   ;; Handle different kinds of inputs
   (cond
    ;; Ignore empty input
@@ -4875,7 +5127,7 @@ This returns non-nil only iff we actually send anything."
       (run-hook-with-args 'erc-send-pre-hook input)
       (when erc-send-this
        (if (or (string-match "\n" str)
-               (not (char-equal (aref str 0) ?/)))
+               (not (string-match erc-command-regexp str)))
            (mapc
             (lambda (line)
               (mapc
@@ -4904,7 +5156,8 @@ This returns non-nil only iff we actually send anything."
        (erc-put-text-property beg (point)
                               'face 'erc-command-indicator-face)
        (insert "\n"))
-      (set-marker (process-mark erc-server-process) (point))
+      (when (processp erc-server-process)
+       (set-marker (process-mark erc-server-process) (point)))
       (set-marker erc-insert-marker (point))
       (save-excursion
        (save-restriction
@@ -4923,7 +5176,8 @@ current position."
        (erc-put-text-property beg (point)
                               'face 'erc-input-face))
       (insert "\n")
-      (set-marker (process-mark erc-server-process) (point))
+      (when (processp erc-server-process)
+       (set-marker (process-mark erc-server-process) (point)))
       (set-marker erc-insert-marker (point))
       (save-excursion
        (save-restriction
@@ -4932,7 +5186,7 @@ current position."
          (run-hooks 'erc-send-post-hook))))))
 
 (defun erc-command-symbol (command)
-  "Return the erc command symbol for COMMAND if it exists and is bound."
+  "Return the ERC command symbol for COMMAND if it exists and is bound."
   (let ((cmd (intern-soft (format "erc-cmd-%s" (upcase command)))))
     (when (fboundp cmd) cmd)))
 
@@ -4940,7 +5194,7 @@ current position."
   "Extract command and args from the input LINE.
 If no command was given, return nil.  If command matches, return a
 list of the form: (command args) where both elements are strings."
-  (when (string-match "^/\\([A-Za-z]+\\)\\(\\s-+.*\\|\\s-*\\)$" line)
+  (when (string-match erc-command-regexp line)
     (let* ((cmd (erc-command-symbol (match-string 1 line)))
           ;; note: return is nil, we apply this simply for side effects
           (canon-defun (while (and cmd (symbolp (symbol-function cmd)))
@@ -4975,8 +5229,9 @@ strings over to the next call."
 
 (defun erc-set-current-nick (nick)
   "Set the current nickname to NICK."
-  (with-current-buffer (or (erc-server-buffer)
-                          (current-buffer))
+  (with-current-buffer (if (buffer-live-p (erc-server-buffer))
+                          (erc-server-buffer)
+                        (current-buffer))
     (setq erc-server-current-nick nick)))
 
 (defun erc-current-nick ()
@@ -5031,7 +5286,7 @@ See also `erc-downcase'."
 (defun erc-add-query (nickname)
   "Add QUERY'd NICKNAME to the default channel list.
 
-The previous default target of QUERY type gets removed"
+The previous default target of QUERY type gets removed."
   (let ((d1 (car erc-default-recipients))
        (d2 (cdr erc-default-recipients))
        (qt (cons 'QUERY (downcase nickname))))
@@ -5056,10 +5311,10 @@ The previous default target of QUERY type gets removed"
 Takes a full SPEC of a user in the form \"nick!login@host\", and
 matches against all the regexp's in `erc-ignore-list'.  If any
 match, returns that regexp."
-  (dolist (ignored (with-current-buffer (erc-server-buffer) erc-ignore-list))
-    (if (string-match ignored spec)
-       ;; We have `require'd cl, so we can return from the block named nil
-       (return ignored))))
+  (catch 'found
+    (dolist (ignored (erc-with-server-buffer erc-ignore-list))
+      (if (string-match ignored spec)
+         (throw 'found ignored)))))
 
 (defun erc-ignored-reply-p (msg tgt proc)
   ;; FIXME: this docstring needs fixing -- Lawrence 2004-01-08
@@ -5220,6 +5475,7 @@ If CHANNEL is non-nil, toggle MODE for that channel, otherwise use
   (save-excursion
     (let ((inhibit-read-only t))
       (set-text-properties start end nil object))))
+(put 'erc-remove-text-properties-region 'disabled t)
 
 ;; script execution and startup
 
@@ -5241,13 +5497,11 @@ If FILE is found, return the path to it."
 (defun erc-select-startup-file ()
   "Select an ERC startup file.
 See also `erc-startup-file-list'."
-  (let ((l erc-startup-file-list)
-       (f nil))
-    (while (and (not f) l)
-      (if (file-readable-p (car l))
-         (setq f (car l)))
-      (setq l (cdr l)))
-    f))
+  (catch 'found
+    (dolist (f erc-startup-file-list)
+      (setq f (convert-standard-filename f))
+      (when (file-readable-p f)
+       (throw 'found f)))))
 
 (defun erc-find-script-file (file)
   "Search for FILE in `default-directory', and any in `erc-script-path'."
@@ -5258,8 +5512,8 @@ See also `erc-startup-file-list'."
 
 FILE must be the full name, it is not searched in the
 `erc-script-path'.  If the filename ends with `.el', then load it
-as a emacs-lisp program.  Otherwise, treat it as a regular IRC
-script"
+as an Emacs Lisp program.  Otherwise, treat it as a regular IRC
+script."
   (erc-log (concat "erc-load-script: " file))
   (cond
    ((string-match "\\.el$" file)
@@ -5271,13 +5525,13 @@ script"
   "Process an IRC script LINE.
 
 Does script-specific substitutions (script arguments, current nick,
-server, etc.)  in LINE and returns it.
+server, etc.) in LINE and returns it.
 
 Substitutions are: %C and %c = current target (channel or nick),
 %S %s = current server, %N %n = my current nick, and %x is x verbatim,
 where x is any other character;
 $* = the entire argument string, $1 = the first argument, $2 = the second,
-end so on."
+and so on."
   (if (not args) (setq args ""))
   (let* ((arg-esc-regexp "\\(\\$\\(\\*\\|[1-9][0-9]*\\)\\)\\([^0-9]\\|$\\)")
         (percent-regexp "\\(%.\\)")
@@ -5381,7 +5635,7 @@ user input."
   (erc-log (format "login: nick: %s, user: %s %s %s :%s"
                   (erc-current-nick)
                   (user-login-name)
-                  (system-name)
+                  (or erc-system-name (system-name))
                   erc-session-server
                   erc-session-user-full-name))
   (if erc-session-password
@@ -5402,10 +5656,10 @@ user input."
   "Determine the connection and authentication parameters.
 Sets the buffer local variables:
 
-- erc-session-server
-- erc-session-port
-- erc-session-full-name
-- erc-server-current-nick"
+- `erc-session-server'
+- `erc-session-port'
+- `erc-session-full-name'
+- `erc-server-current-nick'"
   (setq erc-session-server (erc-compute-server server)
        erc-session-port (or port erc-default-port)
        erc-session-user-full-name (erc-compute-full-name name))
@@ -5417,7 +5671,7 @@ Sets the buffer local variables:
 This tries a number of increasingly more default methods until a
 non-nil value is found.
 
-- SERVER (the argument passwd to this function)
+- SERVER (the argument passed to this function)
 - The `erc-server' option
 - The value of the IRCSERVER environment variable
 - The `erc-default-server' variable"
@@ -5547,7 +5801,7 @@ See also `erc-emacs-time-to-erc-time'."
 
 (defun erc-client-info (s)
   "Return CTCP CLIENTINFO on command S.
-If S is NIL or an empty string then return general CLIENTINFO"
+If S is nil or an empty string then return general CLIENTINFO."
   (if (or (not s) (string= s ""))
       (concat
        (apply #'concat
@@ -5613,16 +5867,16 @@ entry of `channel-members'."
                             ""))
        user))))
 
-(defun erc-away-p ()
-  "Return t if the current ERC process is set away."
-  (save-excursion
-    (and (erc-server-buffer-live-p)
-        (set-buffer (process-buffer erc-server-process))
-        erc-away)))
+(defun erc-away-time ()
+  "Return non-nil if the current ERC process is set away.
+
+In particular, the time that we were set away is returned.
+See `current-time' for details on the time format."
+  (erc-with-server-buffer erc-away))
 
 ;; Mode line handling
 
-(defcustom erc-mode-line-format "%s %a"
+(defcustom erc-mode-line-format "%S %a"
   "A string to be formatted and shown in the mode-line in `erc-mode'.
 
 The string is formatted using `format-spec' and the result is set as the value
@@ -5630,24 +5884,42 @@ of `mode-line-buffer-identification'.
 
 The following characters are replaced:
 %a: String indicating away status or \"\" if you are not away
+%l: The estimated lag time to the server
 %m: The modes of the channel
 %n: The current nick name
+%N: The name of the network
 %o: The topic of the channel
 %p: The session port
 %t: The name of the target (channel, nickname, or servername:port)
 %s: In the server-buffer, this gets filled with the value of
     `erc-server-announced-name', in a channel, the value of
-    (erc-default-target) also get concatenated."
+    (erc-default-target) also get concatenated.
+%S: In the server-buffer, this gets filled with the value of
+    `erc-network', in a channel, the value of (erc-default-target)
+    also get concatenated."
   :group 'erc-mode-line-and-header
   :type 'string)
 
-(defcustom erc-header-line-format "[IRC] %n on %t %m %o"
+(defcustom erc-header-line-format "%n on %t (%m,%l) %o"
   "A string to be formatted and shown in the header-line in `erc-mode'.
-Only used in Emacs 21.
+Only used starting in Emacs 21.
+
+Set this to nil if you do not want the header line to be
+displayed.
 
 See `erc-mode-line-format' for which characters are can be used."
   :group 'erc-mode-line-and-header
-  :type 'string)
+  :set (lambda (sym val)
+        (set sym val)
+        (when (fboundp 'erc-update-mode-line)
+          (erc-update-mode-line nil)))
+  :type '(choice (const :tag "Disabled" nil)
+                string))
+
+(defcustom erc-header-line-uses-tabbar-p nil
+  "Use tabbar mode instead of the header line to display the header."
+  :group 'erc-mode-line-and-header
+  :type 'boolean)
 
 (defcustom erc-header-line-uses-help-echo-p t
   "Show the contents of the header line in the echo area or as a tooltip
@@ -5667,13 +5939,14 @@ Otherwise, use the `erc-header-line' face."
                 (function :tag "Call a function")))
 
 (defcustom erc-show-channel-key-p t
-  "Show the the channel key in the header line."
+  "Show the channel key in the header line."
   :group 'erc-paranoia
   :type 'boolean)
 
 (defcustom erc-common-server-suffixes
   '(("openprojects.net$" . "OPN")
-    ("freenode.net$" . "OPN"))
+    ("freenode.net$" . "freenode")
+    ("oftc.net$" . "OFTC"))
   "Alist of common server name suffixes.
 This variable is used in mode-line display to save screen
 real estate.  Set it to nil if you want to avoid changing
@@ -5685,7 +5958,7 @@ displayed hostnames."
   "(AWAY since %a %b %d %H:%M) "
   "When you're away on a server, this is shown in the mode line.
 This should be a string with substitution variables recognized by
-format-time-message."
+`format-time-string'."
   :group 'erc-mode-line-and-header
   :type 'string)
 
@@ -5722,49 +5995,79 @@ format-time-message."
          (server-name server-name)
          (t (buffer-name (current-buffer))))))
 
+(defun erc-format-network ()
+  "Return the name of the network we are currently on."
+  (let ((network (and (fboundp 'erc-network-name) (erc-network-name))))
+    (if (and network (symbolp network))
+       (symbol-name network)
+      "")))
+
+(defun erc-format-target-and/or-network ()
+  "Return the network or the current target and network combined.
+If the name of the network is not available, then use the
+shortened server name instead."
+  (let ((network-name (or (and (fboundp 'erc-network-name) (erc-network-name))
+                         (erc-shorten-server-name
+                          (or erc-server-announced-name
+                              erc-session-server)))))
+    (when (and network-name (symbolp network-name))
+      (setq network-name (symbol-name network-name)))
+    (cond ((erc-default-target)
+          (concat (erc-string-no-properties (erc-default-target))
+                  "@" network-name))
+         (network-name network-name)
+         (t (buffer-name (current-buffer))))))
+
 (defun erc-format-away-status ()
   "Return a formatted `erc-mode-line-away-status-format'
 if `erc-away' is non-nil."
-  (let ((a (when (erc-server-buffer-live-p)
-            (with-current-buffer (process-buffer erc-server-process)
-              erc-away))))
+  (let ((a (erc-away-time)))
     (if a
        (format-time-string erc-mode-line-away-status-format a)
       "")))
 
 (defun erc-format-channel-modes ()
-  "Return the current channel's modes and the estimated lag."
-  (let ((lag (when (erc-server-buffer-live-p)
-              (with-current-buffer (process-buffer erc-server-process)
-                erc-server-lag))))
-    (concat (apply 'concat
-                  "(+" erc-channel-modes)
-           (cond ((and erc-channel-user-limit erc-channel-key)
-                  (if erc-show-channel-key-p
-                      (format "lk %.0f %s" erc-channel-user-limit
-                              erc-channel-key)
-                    (format "kl %.0f" erc-channel-user-limit)))
-                 (erc-channel-user-limit
-                  ;; Emacs has no bignums
-                  (format "l %.0f" erc-channel-user-limit))
-                 (erc-channel-key
-                  (if erc-show-channel-key-p
-                      (format "k %s" erc-channel-key)
-                    "k"))
-                 (t ""))
-           (if lag (format ",lag:%.0f" lag) "")
-           ")")))
+  "Return the current channel's modes."
+  (concat (apply 'concat
+                "+" erc-channel-modes)
+         (cond ((and erc-channel-user-limit erc-channel-key)
+                (if erc-show-channel-key-p
+                    (format "lk %.0f %s" erc-channel-user-limit
+                            erc-channel-key)
+                  (format "kl %.0f" erc-channel-user-limit)))
+               (erc-channel-user-limit
+                ;; Emacs has no bignums
+                (format "l %.0f" erc-channel-user-limit))
+               (erc-channel-key
+                (if erc-show-channel-key-p
+                    (format "k %s" erc-channel-key)
+                  "k"))
+               (t nil))))
+
+(defun erc-format-lag-time ()
+  "Return the estimated lag time to server, `erc-server-lag'."
+  (let ((lag (erc-with-server-buffer erc-server-lag)))
+    (cond (lag (format "lag:%.0f" lag))
+         (t ""))))
+
+;; erc-goodies is required at end of this file.
+(declare-function erc-controls-strip "erc-goodies" (str))
+
+(defvar tabbar--local-hlf)
 
 (defun erc-update-mode-line-buffer (buffer)
   "Update the mode line in a single ERC buffer BUFFER."
   (with-current-buffer buffer
     (let ((spec (format-spec-make
                 ?a (erc-format-away-status)
+                ?l (erc-format-lag-time)
                 ?m (erc-format-channel-modes)
                 ?n (or (erc-current-nick) "")
+                ?N (erc-format-network)
                 ?o (erc-controls-strip erc-channel-topic)
                 ?p (erc-port-to-string erc-session-port)
                 ?s (erc-format-target-and/or-server)
+                ?S (erc-format-target-and/or-network)
                 ?t (erc-format-target)))
          (process-status (cond ((and (erc-server-process-alive)
                                      (not erc-server-connected))
@@ -5778,7 +6081,7 @@ if `erc-away' is non-nil."
                      ((functionp erc-header-line-face-method)
                       (funcall erc-header-line-face-method))
                      (t
-                      erc-header-line))))
+                      'erc-header-line))))
       (cond ((featurep 'xemacs)
             (setq modeline-buffer-identification
                   (list (format-spec erc-mode-line-format spec)))
@@ -5791,7 +6094,11 @@ if `erc-away' is non-nil."
        (let ((header (if erc-header-line-format
                          (format-spec erc-header-line-format spec)
                        nil)))
-         (cond ((null header)
+         (cond (erc-header-line-uses-tabbar-p
+                (set (make-local-variable 'tabbar--local-hlf)
+                     header-line-format)
+                (kill-local-variable 'header-line-format))
+               ((null header)
                 (setq header-line-format nil))
                (erc-header-line-uses-help-echo-p
                 (let ((help-echo (with-temp-buffer
@@ -5806,7 +6113,10 @@ if `erc-away' is non-nil."
                              (erc-propertize header 'help-echo help-echo
                                              'face face)
                            (erc-propertize header 'help-echo help-echo))))))
-               (t (setq header-line-format header))))))
+               (t (setq header-line-format
+                        (if face
+                            (erc-propertize header 'face face)
+                          header)))))))
     (if (featurep 'xemacs)
        (redraw-modeline)
       (force-mode-line-update))))
@@ -5832,10 +6142,12 @@ P may be an integer or a service name."
 
 (defun erc-string-to-port (s)
   "Convert string S to either an integer port number or a service name."
-  (let ((n (string-to-number s)))
-    (if (= n 0)
-       s
-      n)))
+  (if (numberp s)
+      s
+    (let ((n (string-to-number s)))
+      (if (= n 0)
+         s
+       n))))
 
 (defun erc-version (&optional here)
   "Show the version number of ERC in the minibuffer.
@@ -5885,8 +6197,8 @@ If optional argument HERE is non-nil, insert version number at point."
 
 All windows are opened in the current frame."
   (interactive)
-  (unless (boundp 'erc-server-process)
-    (error "No erc-process found in current buffer"))
+  (unless erc-server-process
+    (error "No erc-server-process found in current buffer"))
   (let ((bufs (erc-buffer-list nil erc-server-process)))
     (when bufs
       (delete-other-windows)
@@ -5894,12 +6206,13 @@ All windows are opened in the current frame."
       (setq bufs (cdr bufs))
       (while bufs
        (split-window)
-       (switch-to-buffer-other-window (car bufs))
+       (other-window 1)
+       (switch-to-buffer (car bufs))
        (setq bufs (cdr bufs))
        (balance-windows)))))
 
 (defun erc-popup-input-buffer ()
-  "Provide a input buffer."
+  "Provide an input buffer."
    (interactive)
    (let ((buffer-name (generate-new-buffer-name "*ERC input*"))
         (mode (intern
@@ -5946,12 +6259,17 @@ All windows are opened in the current frame."
    (ctcp-request-to . "==> CTCP request from %n (%u@%h) to %t: %r")
    (ctcp-too-many . "Too many CTCP queries in single message. Ignoring")
    (flood-ctcp-off . "FLOOD PROTECTION: Automatic CTCP responses turned off.")
-   (flood-strict-mode . "FLOOD PROTECTION: Switched to Strict Flood Control mode.")
-   (disconnected . "Connection failed!  Re-establishing connection...")
-   (disconnected-noreconnect . "Connection failed!  Not re-establishing connection.")
+   (flood-strict-mode
+    . "FLOOD PROTECTION: Switched to Strict Flood Control mode.")
+   (disconnected . "\n\nConnection failed!  Re-establishing connection...\n")
+   (disconnected-noreconnect
+    . "\n\nConnection failed!  Not re-establishing connection.\n")
+   (finished . "\n\n*** ERC finished ***\n")
+   (terminated . "\n\n*** ERC terminated: %e\n")
    (login . "Logging in as \'%n\'...")
    (nick-in-use . "%n is in use. Choose new nickname: ")
-   (nick-too-long . "WARNING: Nick length (%i) exceeds max NICKLEN(%l) defined by server")
+   (nick-too-long
+    . "WARNING: Nick length (%i) exceeds max NICKLEN(%l) defined by server")
    (no-default-channel . "No default channel")
    (no-invitation . "You've got no invitation")
    (no-target . "No target")
@@ -5989,10 +6307,12 @@ All windows are opened in the current frame."
    (s252   . "%i operator(s) online")
    (s253   . "%i unknown connection(s)")
    (s254   . "%i channels formed")
+   (s275   . "%n %m")
    (s301   . "%n is AWAY: %r")
    (s303   . "Is online: %n")
    (s305   . "%m")
    (s306   . "%m")
+   (s307   . "%n %m")
    (s311   . "%n is %f (%u@%h)")
    (s312   . "%n is/was on server %s (%c)")
    (s313   . "%n is an IRC operator")
@@ -6004,6 +6324,7 @@ All windows are opened in the current frame."
    (s321   . "Channel  Users  Topic")
    (s322   . "%c [%u] %t")
    (s324   . "%c modes: %m")
+   (s328   . "%c URL: %u")
    (s329   . "%c was created on %t")
    (s330   . "%n %a %i")
    (s331   . "No topic is set for %c")
@@ -6012,7 +6333,8 @@ All windows are opened in the current frame."
    (s341   . "Inviting %n to channel %c")
    (s352   . "%-11c %-10n %-4a %u@%h (%f)")
    (s353   . "Users on %c: %u")
-   (s367   . "Ban on %b on %c set by %s on %t (Use /banlist!)")
+   (s367   . "Ban for %b on %c")
+   (s367-set-by . "Ban for %b on %c set by %s on %t")
    (s368   . "Banlist of %c ends.")
    (s379   . "%c: Forwarded to %f")
    (s391   . "The time at %s is %t")
@@ -6060,7 +6382,8 @@ functions."
       (format "%s (%s@%s) has left channel %s%s"
              nick user host channel
              (if (not (string= reason ""))
-                 (format ": %s" reason)
+                 (format ": %s"
+                         (erc-replace-regexp-in-string "%" "%%" reason))
                "")))))
 
 
@@ -6151,6 +6474,17 @@ This function should be on `erc-kill-channel-hook'."
 
 ;;; Dealing with `erc-parsed'
 
+(defun erc-find-parsed-property ()
+  "Find the next occurrence of the `erc-parsed' text property."
+  (text-property-not-all (point-min) (point-max) 'erc-parsed nil))
+
+(defun erc-restore-text-properties ()
+  "Restore the property 'erc-parsed for the region."
+  (let ((parsed-posn (erc-find-parsed-property)))
+    (put-text-property
+     (point-min) (point-max)
+     'erc-parsed (when parsed-posn (erc-get-parsed-vector parsed-posn)))))
+
 (defun erc-get-parsed-vector (point)
   "Return the whole parsed vector on POINT."
   (get-text-property point 'erc-parsed))
@@ -6182,15 +6516,14 @@ Otherwise, connect to HOST:PORT as USER and /join CHANNEL."
               (lambda ()
                 (and (string-equal erc-session-server host)
                      (= erc-session-port port)
-                     erc-server-connected
-                     (eq (erc-server-buffer) (current-buffer))))))))
+                     (erc-open-server-buffer-p)))))))
     (with-current-buffer (or server-buffer (current-buffer))
       (if (and server-buffer channel)
          (erc-cmd-JOIN channel)
-       (erc host port (or user (erc-compute-nick)) (erc-compute-full-name)
-            (not server-buffer) password nil channel
-            (when server-buffer
-              (get-buffer-process server-buffer)))))))
+       (erc-open host port (or user (erc-compute-nick)) (erc-compute-full-name)
+                 (not server-buffer) password nil channel
+                 (when server-buffer
+                   (get-buffer-process server-buffer)))))))
 
 (provide 'erc)