Replace `iff' in doc-strings and comments.
[bpt/emacs.git] / lisp / erc / erc.el
index 350a77b..c26bdf2 100644 (file)
@@ -17,7 +17,7 @@
 
 ;; 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)
+;; the Free Software Foundation; either version 3, or (at your option)
 ;; any later version.
 
 ;; GNU Emacs is distributed in the hope that it will be useful,
@@ -66,7 +66,7 @@
 
 ;;; Code:
 
-(defconst erc-version-string "Version 5.2 stable pre-release"
+(defconst erc-version-string "Version 5.2"
   "ERC version.  This is used by function `erc-version'.")
 
 (eval-when-compile (require 'cl))
@@ -324,7 +324,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)
 
@@ -355,6 +355,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
@@ -381,14 +392,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)
@@ -399,7 +410,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)
@@ -410,7 +421,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))
@@ -503,16 +514,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."
@@ -527,16 +537,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."
@@ -833,7 +842,7 @@ See `erc-server-flood-margin' for other flood-related parameters.")
 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
+script and it gets (load)ed.  Otherwise it is treated as a bunch of
 regular IRC commands."
   :group 'erc-scripts
   :type '(repeat file))
@@ -1282,7 +1291,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)
@@ -1305,7 +1314,7 @@ 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)
@@ -1339,6 +1348,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."
@@ -1351,7 +1368,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
@@ -1401,7 +1418,7 @@ server 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)))))
@@ -1584,10 +1601,10 @@ 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)
@@ -1597,7 +1614,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)
@@ -1652,8 +1669,8 @@ 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 ((pre (make-symbol "pre"))
+       (pro (make-symbol "pro")))
     `(let ((,pro ,process)
           (,pre ,pred))
        (mapcar (lambda (buffer)
@@ -1736,7 +1753,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
@@ -1794,43 +1814,43 @@ 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
-     "Mark unidentified users on freenode and other servers supporting CAPAB"
-     capab-identify)
-    (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 "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 "Save buffers in logs" log)
-    (const :tag "Highlight pals, fools, and other keywords" match)
-    (const :tag "Display a menu in ERC buffers" menu)
-    (const :tag "Detect netsplits" netsplit)
-    (const :tag "Don't display non-IRC commands after evaluation"
+    (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 "netsplit: Detect netsplits" netsplit)
+    (const :tag "noncommands: Don't display non-IRC commands after evaluation"
           noncommands)
-    (const :tag "Notify when the online status of certain users changes"
+    (const :tag
+          "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)
     (repeat :tag "Others" :inline t symbol))
   :group 'erc)
 
@@ -1883,9 +1903,7 @@ removed from the list will be disabled."
 
 (defun erc-open (&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.
+  "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
@@ -1950,10 +1968,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
@@ -1968,25 +1982,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 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
@@ -1997,11 +2011,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
 
@@ -2101,8 +2117,12 @@ functions in here get called with the parameters SERVER and NICK."
                  (nick   (erc-compute-nick))
                  password
                  (full-name (erc-compute-full-name)))
-  "Select connection parameters and run ERC.
-Non-interactively, it takes keyword arguments
+  "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))
@@ -2113,12 +2133,13 @@ That is, if called with
 
    (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))
 
+;;;###autoload
 (defalias 'erc-select 'erc)
 
 (defun erc-ssl (&rest r)
@@ -2409,7 +2430,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)
@@ -2567,20 +2588,19 @@ If no USER argument is specified, list the contents of `erc-ignore-list'."
        (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)
+  (let ((ignored-nick (car (erc-with-server-buffer
                             (erc-member-ignore-case (regexp-quote user)
                                                     erc-ignore-list)))))
     (unless ignored-nick
@@ -2595,7 +2615,7 @@ If no USER argument is specified, list the contents of `erc-ignore-list'."
       (erc-display-line
        (erc-make-notice (format "No longer ignoring %s" user))
        'active)
-      (with-current-buffer (erc-server-buffer)
+      (erc-with-server-buffer
        (setq erc-ignore-list (delete ignored-nick erc-ignore-list)))))
   t)
 
@@ -2654,8 +2674,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)
@@ -2847,10 +2867,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
@@ -3008,8 +3027,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
@@ -3148,7 +3167,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
@@ -3156,10 +3176,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)))
 
@@ -3170,9 +3198,7 @@ the message given by REASON."
 
 (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-with-all-buffers-of-server nil #'erc-open-server-buffer-p
                                  (erc-cmd-QUIT reason)))
 
 (defalias 'erc-cmd-GQ 'erc-cmd-GQUIT)
@@ -3180,8 +3206,17 @@ the message given by REASON."
 
 (defun erc-cmd-RECONNECT ()
   "Try to reconnect to the current IRC server."
-  (setq erc-server-reconnect-count 0)
-  (erc-server-reconnect)
+  (let ((buffer (or (erc-server-buffer) (current-buffer)))
+       (process nil))
+    (with-current-buffer (if (bufferp buffer) buffer (current-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)
 
 (defun erc-cmd-SERVER (server)
@@ -3354,7 +3389,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
@@ -3424,7 +3459,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
@@ -3718,7 +3753,7 @@ To change how this query window is displayed, use `let' to bind
     (erc-update-mode-line)
     buf))
 
-(defcustom erc-auto-query nil
+(defcustom erc-auto-query 'bury
   "If non-nil, create a query buffer each time you receive a private message.
 
 If the buffer doesn't already exist it is created.  This can be
@@ -3792,7 +3827,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
@@ -3923,7 +3958,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)
@@ -3947,7 +3982,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)
@@ -3964,7 +3999,7 @@ 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',
@@ -3978,7 +4013,7 @@ 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.
 
@@ -3994,7 +4029,7 @@ 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',
@@ -4082,24 +4117,29 @@ See also: `erc-echo-notice-in-user-buffers',
   "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))))))
+  (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.
@@ -4326,14 +4366,12 @@ If non-nil, return from being away."
                          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
@@ -4900,7 +4938,7 @@ Specifically, return the position of `erc-insert-marker'."
 (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
@@ -5018,8 +5056,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 ()
@@ -5100,7 +5139,7 @@ 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."
   (catch 'found
-    (dolist (ignored (with-current-buffer (erc-server-buffer) erc-ignore-list))
+    (dolist (ignored (erc-with-server-buffer erc-ignore-list))
       (if (string-match ignored spec)
          (throw 'found ignored)))))
 
@@ -5654,12 +5693,12 @@ 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
 
@@ -5687,9 +5726,17 @@ The following characters are replaced:
   "A string to be formatted and shown in the header-line in `erc-mode'.
 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-help-echo-p t
   "Show the contents of the header line in the echo area or as a tooltip
@@ -5709,13 +5756,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
@@ -5767,9 +5815,7 @@ This should be a string with substitution variables recognized by
 (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)
       "")))
@@ -5794,9 +5840,7 @@ if `erc-away' is non-nil."
 
 (defun erc-format-lag-time ()
   "Return the estimated lag time to server, `erc-server-lag'."
-  (let ((lag (when (erc-server-buffer-live-p)
-              (with-current-buffer (process-buffer erc-server-process)
-                erc-server-lag))))
+  (let ((lag (erc-with-server-buffer erc-server-lag)))
     (cond (lag (format "lag:%.0f" lag))
          (t ""))))
 
@@ -6118,7 +6162,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))
                "")))))
 
 
@@ -6213,6 +6258,13 @@ This function should be on `erc-kill-channel-hook'."
   "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))
@@ -6244,8 +6296,7 @@ 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)