]> code.delx.au - gnu-emacs/blobdiff - lisp/net/browse-url.el
* net/tramp.el (tramp-find-inline-compress)
[gnu-emacs] / lisp / net / browse-url.el
index 04f83ed465a59c30f731fe140efbdca9c46e3b9b..14d58cf4466cb8f8dba90a7ea554211b680903a8 100644 (file)
@@ -1,7 +1,7 @@
 ;;; browse-url.el --- pass a URL to a WWW browser
 
 ;; Copyright (C) 1995, 1996, 1997, 1998, 1999, 2000, 2001, 2002,
 ;;; browse-url.el --- pass a URL to a WWW browser
 
 ;; Copyright (C) 1995, 1996, 1997, 1998, 1999, 2000, 2001, 2002,
-;;   2003, 2004, 2005, 2006, 2007 Free Software Foundation, Inc.
+;;   2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010, 2011 Free Software Foundation, Inc.
 
 ;; Author: Denis Howe <dbh@doc.ic.ac.uk>
 ;; Maintainer: FSF
 
 ;; Author: Denis Howe <dbh@doc.ic.ac.uk>
 ;; Maintainer: FSF
 
 ;; This file is part of GNU Emacs.
 
 
 ;; 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
 ;; it under the terms of the GNU General Public License as published by
-;; the Free Software Foundation; either version 3, 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 Emacs is distributed in the hope that it will be useful,
 ;; but WITHOUT ANY WARRANTY; without even the implied warranty of
@@ -21,9 +21,7 @@
 ;; GNU General Public License for more details.
 
 ;; You should have received a copy of the GNU General Public License
 ;; 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:
 
 
 ;;; Commentary:
 
 ;; browse-url-cci                     XMosaic     2.5
 ;; browse-url-w3                      w3          0
 ;; browse-url-w3-gnudoit              w3 remotely
 ;; browse-url-cci                     XMosaic     2.5
 ;; browse-url-w3                      w3          0
 ;; browse-url-w3-gnudoit              w3 remotely
-;; browse-url-iximosaic               IXI Mosaic  ?
-;; browse-url-lynx-*                 Lynx           0
-;; browse-url-grail                   Grail       0.3b1
-;; browse-url-mmm                     MMM         ?
+;; browse-url-text-*                 Any text browser     0
 ;; browse-url-generic                 arbitrary
 ;; browse-url-default-windows-browser MS-Windows browser
 ;; browse-url-default-macosx-browser  Mac OS X browser
 ;; browse-url-gnome-moz               GNOME interface to Mozilla
 ;; browse-url-kde                     KDE konqueror (kfm)
 ;; browse-url-generic                 arbitrary
 ;; browse-url-default-windows-browser MS-Windows browser
 ;; browse-url-default-macosx-browser  Mac OS X browser
 ;; browse-url-gnome-moz               GNOME interface to Mozilla
 ;; browse-url-kde                     KDE konqueror (kfm)
+;; browse-url-elinks                  Elinks      Don't know (tried with 0.12.GIT)
 
 ;; [A version of the Netscape browser is now free software
 ;; <URL:http://www.mozilla.org/>, albeit not GPLed, so it is
 
 ;; [A version of the Netscape browser is now free software
 ;; <URL:http://www.mozilla.org/>, albeit not GPLed, so it is
@@ -71,7 +67,7 @@
 ;; control but which window DO you want to control and how do you
 ;; discover its id?
 
 ;; control but which window DO you want to control and how do you
 ;; discover its id?
 
-;; William M. Perry's excellent "w3" WWW browser for
+;; William M.  Perry's excellent "w3" WWW browser for
 ;; Emacs <URL:ftp://cs.indiana.edu/pub/elisp/w3/>
 ;; has a function w3-follow-url-at-point, but that
 ;; doesn't let you edit the URL like browse-url.
 ;; Emacs <URL:ftp://cs.indiana.edu/pub/elisp/w3/>
 ;; has a function w3-follow-url-at-point, but that
 ;; doesn't let you edit the URL like browse-url.
 ;; Emacs process is available from
 ;; <URL:ftp://ftp.splode.com/pub/users/friedman/packages/>.
 
 ;; Emacs process is available from
 ;; <URL:ftp://ftp.splode.com/pub/users/friedman/packages/>.
 
-;; Grail is the freely available WWW browser implemented in Python, a
-;; cool object-oriented freely available interpreted language.  Grail
-;; 0.3b1 was the first version to have remote control as distributed.
-;; For more information on Grail see
-;; <URL:http://grail.cnri.reston.va.us/> and for more information on
-;; Python see <url:http://www.python.org/>.  Grail support in
-;; browse-url.el written by Barry Warsaw <bwarsaw@python.org>.
-
 ;; Lynx is now distributed by the FSF.  See also
 ;; <URL:http://lynx.browser.org/>.
 
 ;; Lynx is now distributed by the FSF.  See also
 ;; <URL:http://lynx.browser.org/>.
 
 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
 ;; Variables
 
 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
 ;; Variables
 
-(eval-when-compile (require 'thingatpt)
+(eval-when-compile (require 'cl)
+                  (require 'thingatpt)
                    (require 'term)
                   (require 'dired)
                    (require 'executable)
                    (require 'term)
                   (require 'dired)
                    (require 'executable)
@@ -255,14 +244,12 @@ regexp should probably be \".\" to specify a default browser."
          (function-item :tag "Netscape" :value  browse-url-netscape)
          (function-item :tag "Mosaic" :value  browse-url-mosaic)
          (function-item :tag "Mosaic using CCI" :value  browse-url-cci)
          (function-item :tag "Netscape" :value  browse-url-netscape)
          (function-item :tag "Mosaic" :value  browse-url-mosaic)
          (function-item :tag "Mosaic using CCI" :value  browse-url-cci)
-         (function-item :tag "IXI Mosaic" :value  browse-url-iximosaic)
-         (function-item :tag "Lynx in an xterm window"
-                        :value browse-url-lynx-xterm)
-         (function-item :tag "Lynx in an Emacs window"
-                        :value browse-url-lynx-emacs)
-         (function-item :tag "Grail" :value  browse-url-grail)
-         (function-item :tag "MMM" :value  browse-url-mmm)
+         (function-item :tag "Text browser in an xterm window"
+                        :value browse-url-text-xterm)
+         (function-item :tag "Text browser in an Emacs window"
+                        :value browse-url-text-emacs)
          (function-item :tag "KDE" :value browse-url-kde)
          (function-item :tag "KDE" :value browse-url-kde)
+         (function-item :tag "Elinks" :value browse-url-elinks)
          (function-item :tag "Specified by `Browse Url Generic Program'"
                         :value browse-url-generic)
          (function-item :tag "Default Windows browser"
          (function-item :tag "Specified by `Browse Url Generic Program'"
                         :value browse-url-generic)
          (function-item :tag "Default Windows browser"
@@ -326,7 +313,7 @@ Defaults to the value of `browse-url-mozilla-arguments' at the time
   :group 'browse-url)
 
 ;;;###autoload
   :group 'browse-url)
 
 ;;;###autoload
-(defcustom browse-url-firefox-program "firefox"
+(defcustom browse-url-firefox-program (purecopy "firefox")
   "The name by which to invoke Firefox."
   :type 'string
   :group 'browse-url)
   "The name by which to invoke Firefox."
   :type 'string
   :group 'browse-url)
@@ -344,7 +331,7 @@ Defaults to the value of `browse-url-firefox-arguments' at the time
   :group 'browse-url)
 
 ;;;###autoload
   :group 'browse-url)
 
 ;;;###autoload
-(defcustom browse-url-galeon-program "galeon"
+(defcustom browse-url-galeon-program (purecopy "galeon")
   "The name by which to invoke Galeon."
   :type 'string
   :group 'browse-url)
   "The name by which to invoke Galeon."
   :type 'string
   :group 'browse-url)
@@ -427,7 +414,7 @@ window."
   :group 'browse-url)
 
 (defcustom browse-url-new-window-flag nil
   :group 'browse-url)
 
 (defcustom browse-url-new-window-flag nil
-  "If non-nil, always open a new browser window with appropriate browsers.
+  "Non-nil means always open a new browser window with appropriate browsers.
 Passing an interactive argument to \\[browse-url], or specific browser
 commands reverses the effect of this variable.  Requires Netscape version
 1.1N or later or XMosaic version 2.5 or later if using those browsers."
 Passing an interactive argument to \\[browse-url], or specific browser
 commands reverses the effect of this variable.  Requires Netscape version
 1.1N or later or XMosaic version 2.5 or later if using those browsers."
@@ -457,9 +444,9 @@ commands reverses the effect of this variable.  Requires Netscape version
     ;; applies.
     ("^/\\([^:@]+@\\)?\\([^:]+\\):/*" . "ftp://\\1\\2/")
     ,@(if (memq system-type '(windows-nt ms-dos cygwin))
     ;; applies.
     ("^/\\([^:@]+@\\)?\\([^:]+\\):/*" . "ftp://\\1\\2/")
     ,@(if (memq system-type '(windows-nt ms-dos cygwin))
-          '(("^\\([a-zA-Z]:\\)[\\/]" . "file:\\1/")
+          '(("^\\([a-zA-Z]:\\)[\\/]" . "file:///\\1/")
             ("^[\\/][\\/]+" . "file://")))
             ("^[\\/][\\/]+" . "file://")))
-    ("^/+" . "file:/"))
+    ("^/+" . "file:///"))
   "An alist of (REGEXP . STRING) pairs used by `browse-url-of-file'.
 Any substring of a filename matching one of the REGEXPs is replaced by
 the corresponding STRING using `replace-match', not treating STRING
   "An alist of (REGEXP . STRING) pairs used by `browse-url-of-file'.
 Any substring of a filename matching one of the REGEXPs is replaced by
 the corresponding STRING using `replace-match', not treating STRING
@@ -479,7 +466,7 @@ address to an HTTP URL:
   :type '(repeat (cons :format "%v"
                        (regexp :tag "Regexp")
                        (string :tag "Replacement")))
   :type '(repeat (cons :format "%v"
                        (regexp :tag "Regexp")
                        (string :tag "Replacement")))
-  :version "20.3"
+  :version "23.1"
   :group 'browse-url)
 
 (defcustom browse-url-save-file nil
   :group 'browse-url)
 
 (defcustom browse-url-save-file nil
@@ -515,7 +502,7 @@ enabled.  The port number should be set in `browse-url-CCI-port'."
 (make-variable-buffer-local 'browse-url-temp-file-name)
 
 (defcustom browse-url-xterm-program "xterm"
 (make-variable-buffer-local 'browse-url-temp-file-name)
 
 (defcustom browse-url-xterm-program "xterm"
-  "The name of the terminal emulator used by `browse-url-lynx-xterm'.
+  "The name of the terminal emulator used by `browse-url-text-xterm'.
 This might, for instance, be a separate color version of xterm."
   :type 'string
   :group 'browse-url)
 This might, for instance, be a separate color version of xterm."
   :type 'string
   :group 'browse-url)
@@ -526,17 +513,6 @@ These might set its size, for instance."
   :type '(repeat (string :tag "Argument"))
   :group 'browse-url)
 
   :type '(repeat (string :tag "Argument"))
   :group 'browse-url)
 
-(defcustom browse-url-lynx-emacs-args (and (not window-system)
-                                           '("-show_cursor"))
-  "A list of strings defining options for Lynx in an Emacs buffer.
-
-The default is none in a window system, otherwise `-show_cursor' to
-indicate the position of the current link in the absence of
-highlighting, assuming the normal default for showing the cursor."
-  :type '(repeat (string :tag "Argument"))
-  :version "20.3"
-  :group 'browse-url)
-
 (defcustom browse-url-gnudoit-program "gnudoit"
   "The name of the `gnudoit' program used by `browse-url-w3-gnudoit'."
   :type 'string
 (defcustom browse-url-gnudoit-program "gnudoit"
   "The name of the `gnudoit' program used by `browse-url-w3-gnudoit'."
   :type 'string
@@ -573,28 +549,47 @@ incompatibly at version 4."
   :type 'number
   :group 'browse-url)
 
   :type 'number
   :group 'browse-url)
 
-(defcustom browse-url-lynx-input-field 'avoid
-  "Action on selecting an existing Lynx buffer at an input field.
-What to do when sending a new URL to an existing Lynx buffer in Emacs
-if the Lynx cursor is on an input field (in which case the `g' command
+(defcustom browse-url-text-browser "lynx"
+  "The name of the text browser to invoke."
+  :type 'string
+  :group 'browse-url
+  :version "23.1")
+
+(defcustom browse-url-text-emacs-args (and (not window-system)
+                                          '("-show_cursor"))
+  "A list of strings defining options for a text browser in an Emacs buffer.
+
+The default is none in a window system, otherwise `-show_cursor' to
+indicate the position of the current link in the absence of
+highlighting, assuming the normal default for showing the cursor."
+  :type '(repeat (string :tag "Argument"))
+  :version "23.1"
+  :group 'browse-url)
+
+(defcustom browse-url-text-input-field 'avoid
+  "Action on selecting an existing text browser buffer at an input field.
+What to do when sending a new URL to an existing text browser buffer in Emacs
+if the browser cursor is on an input field (in which case the `g' command
 would be entered as data).  Such fields are recognized by the
 would be entered as data).  Such fields are recognized by the
-underlines ____.  Allowed values: nil: disregard it, 'warn: warn the
-user and don't emit the URL, 'avoid: try to avoid the field by moving
+underlines ____.  Allowed values: nil: disregard it, `warn': warn the
+user and don't emit the URL, `avoid': try to avoid the field by moving
 down (this *won't* always work)."
   :type '(choice (const :tag "Move to try to avoid field" :value avoid)
                  (const :tag "Disregard" :value nil)
                  (const :tag "Warn, don't emit URL" :value warn))
 down (this *won't* always work)."
   :type '(choice (const :tag "Move to try to avoid field" :value avoid)
                  (const :tag "Disregard" :value nil)
                  (const :tag "Warn, don't emit URL" :value warn))
-  :version "20.3"
+  :version "23.1"
   :group 'browse-url)
 
   :group 'browse-url)
 
-(defcustom browse-url-lynx-input-attempts 10
-  "How many times to try to move down from a series of lynx input fields."
+(defcustom browse-url-text-input-attempts 10
+  "How many times to try to move down from a series of text browser input fields."
   :type 'integer
   :type 'integer
+  :version "23.1"
   :group 'browse-url)
 
   :group 'browse-url)
 
-(defcustom browse-url-lynx-input-delay 0.2
-  "How many seconds to wait for lynx between moves down from an input field."
+(defcustom browse-url-text-input-delay 0.2
+  "Seconds to wait for a text browser between moves down from an input field."
   :type 'number
   :type 'number
+  :version "23.1"
   :group 'browse-url)
 
 (defcustom browse-url-kde-program "kfmclient"
   :group 'browse-url)
 
 (defcustom browse-url-kde-program "kfmclient"
@@ -608,6 +603,36 @@ down (this *won't* always work)."
   :type '(repeat (string :tag "Argument"))
   :group 'browse-url)
 
   :type '(repeat (string :tag "Argument"))
   :group 'browse-url)
 
+(defcustom browse-url-elinks-wrapper '("xterm" "-e")
+  "*Wrapper command prepended to the Elinks command-line."
+  :type '(repeat (string :tag "Wrapper"))
+  :group 'browse-url)
+
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+;; URL encoding
+
+(defun browse-url-url-encode-chars (text chars)
+  "URL-encode the chars in TEXT that match CHARS.
+CHARS is a regexp-like character alternative (e.g., \"[)$]\")."
+  (let ((encoded-text (copy-sequence text))
+       (s 0))
+    (while (setq s (string-match chars encoded-text s))
+      (setq encoded-text
+           (replace-match (format "%%%x"
+                                  (string-to-char (match-string 0 encoded-text)))
+                          t t encoded-text)
+           s (1+ s)))
+    encoded-text))
+
+(defun browse-url-encode-url (url)
+  "Escape annoying characters in URL.
+The annoying characters are those that can mislead a web browser
+regarding its parameter treatment."
+  ;; FIXME: Is there an actual example of a web browser getting
+  ;; confused?  (This used to encode commas, but at least Firefox
+  ;; handles commas correctly and doesn't accept encoded commas.)
+  (browse-url-url-encode-chars url "[)$]"))
+
 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
 ;; URL input
 
 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
 ;; URL input
 
@@ -646,7 +671,7 @@ for use in `interactive'."
 ;; this macro.  We use that rather than interactive-p because
 ;; use in a keyboard macro should not change this behavior.
 (defmacro browse-url-maybe-new-window (arg)
 ;; this macro.  We use that rather than interactive-p because
 ;; use in a keyboard macro should not change this behavior.
 (defmacro browse-url-maybe-new-window (arg)
-  `(if (or noninteractive (not (called-interactively-p)))
+  `(if (or noninteractive (not (called-interactively-p 'any)))
        ,arg
      browse-url-new-window-flag))
 
        ,arg
      browse-url-new-window-flag))
 
@@ -676,18 +701,17 @@ interactively.  Turn the filename into a URL with function
 (defun browse-url-file-url (file)
   "Return the URL corresponding to FILE.
 Use variable `browse-url-filename-alist' to map filenames to URLs."
 (defun browse-url-file-url (file)
   "Return the URL corresponding to FILE.
 Use variable `browse-url-filename-alist' to map filenames to URLs."
-  (let ((coding (and default-enable-multibyte-characters
+  ;; De-munge Cygwin filenames before passing them to Windows browser.
+  (if (eq system-type 'cygwin)
+      (let ((winfile (with-output-to-string
+                      (call-process "cygpath" nil standard-output
+                                    nil "-m" file))))
+       (setq file (substring winfile 0 -1))))
+  (let ((coding (and (default-value 'enable-multibyte-characters)
                     (or file-name-coding-system
                         default-file-name-coding-system))))
     (if coding (setq file (encode-coding-string file coding))))
                     (or file-name-coding-system
                         default-file-name-coding-system))))
     (if coding (setq file (encode-coding-string file coding))))
-  ;; URL-encode special chars, do % first
-  (let ((s 0))
-    (while (setq s (string-match "%" file s))
-      (setq file (replace-match "%25" t t file)
-           s (1+ s))))
-  (while (string-match "[*\"()',=;? ]" file)
-    (let ((enc (format "%%%x" (aref file (match-beginning 0)))))
-      (setq file (replace-match enc t t file))))
+  (setq file (browse-url-url-encode-chars file "[*\"()',=;?% ]"))
   (dolist (map browse-url-filename-alist)
     (when (and map (string-match (car map) file))
       (setq file (replace-match (cdr map) t nil file))))
   (dolist (map browse-url-filename-alist)
     (when (and map (string-match (car map) file))
       (setq file (replace-match (cdr map) t nil file))))
@@ -754,7 +778,7 @@ narrowed."
 Prompts for a URL, defaulting to the URL at or before point.  Variable
 `browse-url-browser-function' says which browser to use."
   (interactive (browse-url-interactive-arg "URL: "))
 Prompts for a URL, defaulting to the URL at or before point.  Variable
 `browse-url-browser-function' says which browser to use."
   (interactive (browse-url-interactive-arg "URL: "))
-  (unless (interactive-p)
+  (unless (called-interactively-p 'interactive)
     (setq args (or args (list browse-url-new-window-flag))))
   (let ((process-environment (copy-sequence process-environment)))
     ;; When connected to various displays, be careful to use the display of
     (setq args (or args (list browse-url-new-window-flag))))
   (let ((process-environment (copy-sequence process-environment)))
     ;; When connected to various displays, be careful to use the display of
@@ -762,17 +786,20 @@ Prompts for a URL, defaulting to the URL at or before point.  Variable
     ;; which may not even exist any more.
     (if (stringp (frame-parameter (selected-frame) 'display))
         (setenv "DISPLAY" (frame-parameter (selected-frame) 'display)))
     ;; which may not even exist any more.
     (if (stringp (frame-parameter (selected-frame) 'display))
         (setenv "DISPLAY" (frame-parameter (selected-frame) 'display)))
-    (if (functionp browse-url-browser-function)
-        (apply browse-url-browser-function url args)
-      ;; The `function' can be an alist; look down it for first match
-      ;; and apply the function (which might be a lambda).
-      (catch 'done
-        (dolist (bf browse-url-browser-function)
-          (when (string-match (car bf) url)
-            (apply (cdr bf) url args)
-            (throw 'done t)))
-        (error "No browse-url-browser-function matching URL %s"
-               url)))))
+    (if (and (consp browse-url-browser-function)
+            (not (functionp browse-url-browser-function)))
+       ;; The `function' can be an alist; look down it for first match
+       ;; and apply the function (which might be a lambda).
+       (catch 'done
+         (dolist (bf browse-url-browser-function)
+           (when (string-match (car bf) url)
+             (apply (cdr bf) url args)
+             (throw 'done t)))
+         (error "No browse-url-browser-function matching URL %s"
+                url))
+      ;; Unbound symbols go down this leg, since void-function from
+      ;; apply is clearer than wrong-type-argument from dolist.
+      (apply browse-url-browser-function url args))))
 
 ;;;###autoload
 (defun browse-url-at-point (&optional arg)
 
 ;;;###autoload
 (defun browse-url-at-point (&optional arg)
@@ -807,14 +834,17 @@ to use."
 ;; --- Default MS-Windows browser ---
 
 (defvar dos-windows-version)
 ;; --- Default MS-Windows browser ---
 
 (defvar dos-windows-version)
+(declare-function w32-shell-execute "w32fns.c")    ;; Defined in C.
 
 (defun browse-url-default-windows-browser (url &optional new-window)
   (interactive (browse-url-interactive-arg "URL: "))
 
 (defun browse-url-default-windows-browser (url &optional new-window)
   (interactive (browse-url-interactive-arg "URL: "))
-  (if (eq system-type 'ms-dos)
-      (if dos-windows-version
-         (shell-command (concat "start " (shell-quote-argument url)))
-       (error "Browsing URLs is not supported on this system"))
-    (w32-shell-execute "open" url)))
+  (cond ((eq system-type 'ms-dos)
+        (if dos-windows-version
+            (shell-command (concat "start " (shell-quote-argument url)))
+          (error "Browsing URLs is not supported on this system")))
+       ((eq system-type 'cygwin)
+        (call-process "cygstart" nil nil nil url))
+       (t (w32-shell-execute "open" url))))
 
 (defun browse-url-default-macosx-browser (url &optional new-window)
   (interactive (browse-url-interactive-arg "URL: "))
 
 (defun browse-url-default-macosx-browser (url &optional new-window)
   (interactive (browse-url-interactive-arg "URL: "))
@@ -856,24 +886,21 @@ When called non-interactively, optional second argument NEW-WINDOW is
 used instead of `browse-url-new-window-flag'.
 
 The order attempted is gnome-moz-remote, Mozilla, Firefox,
 used instead of `browse-url-new-window-flag'.
 
 The order attempted is gnome-moz-remote, Mozilla, Firefox,
-Galeon, Konqueror, Netscape, Mosaic, IXI Mosaic, Lynx in an
-xterm, MMM, and then W3."
+Galeon, Konqueror, Netscape, Mosaic, Lynx in an xterm, and then W3."
   (apply
   (apply
-    (cond
-     ((executable-find browse-url-gnome-moz-program) 'browse-url-gnome-moz)
-     ((executable-find browse-url-mozilla-program) 'browse-url-mozilla)
-     ((executable-find browse-url-firefox-program) 'browse-url-firefox)
-     ((executable-find browse-url-galeon-program) 'browse-url-galeon)
-     ((executable-find browse-url-kde-program) 'browse-url-kde)
-     ((executable-find browse-url-netscape-program) 'browse-url-netscape)
-     ((executable-find browse-url-mosaic-program) 'browse-url-mosaic)
-     ((executable-find "tellw3b") 'browse-url-iximosaic)
-     ((executable-find browse-url-xterm-program) 'browse-url-lynx-xterm)
-     ((executable-find "mmm") 'browse-url-mmm)
-     ((locate-library "w3") 'browse-url-w3)
-     (t
-      (lambda (&ignore args) (error "No usable browser found"))))
-     url args))
+   (cond
+    ((executable-find browse-url-gnome-moz-program) 'browse-url-gnome-moz)
+    ((executable-find browse-url-mozilla-program) 'browse-url-mozilla)
+    ((executable-find browse-url-firefox-program) 'browse-url-firefox)
+    ((executable-find browse-url-galeon-program) 'browse-url-galeon)
+    ((executable-find browse-url-kde-program) 'browse-url-kde)
+    ((executable-find browse-url-netscape-program) 'browse-url-netscape)
+    ((executable-find browse-url-mosaic-program) 'browse-url-mosaic)
+    ((executable-find browse-url-xterm-program) 'browse-url-text-xterm)
+    ((locate-library "w3") 'browse-url-w3)
+    (t
+     (lambda (&rest ignore) (error "No usable browser found"))))
+   url args))
 
 ;;;###autoload
 (defun browse-url-netscape (url &optional new-window)
 
 ;;;###autoload
 (defun browse-url-netscape (url &optional new-window)
@@ -893,11 +920,7 @@ is loaded in a new tab in an existing window instead.
 When called non-interactively, optional second argument NEW-WINDOW is
 used instead of `browse-url-new-window-flag'."
   (interactive (browse-url-interactive-arg "URL: "))
 When called non-interactively, optional second argument NEW-WINDOW is
 used instead of `browse-url-new-window-flag'."
   (interactive (browse-url-interactive-arg "URL: "))
-  ;; URL encode any `confusing' characters in the URL.  This needs to
-  ;; include at least commas; presumably also close parens and dollars.
-  (while (string-match "[,)$]" url)
-    (setq url (replace-match
-              (format "%%%x" (string-to-char (match-string 0 url))) t t url)))
+  (setq url (browse-url-encode-url url))
   (let* ((process-environment (browse-url-process-environment))
         (process
          (apply 'start-process
   (let* ((process-environment (browse-url-process-environment))
         (process
          (apply 'start-process
@@ -939,7 +962,7 @@ How depends on `browse-url-netscape-version'."
   ;; <peter.kruse@psychologie.uni-regensburg.de>.
   (browse-url-netscape-send (if (>= browse-url-netscape-version 4)
                                "xfeDoCommand(reload)"
   ;; <peter.kruse@psychologie.uni-regensburg.de>.
   (browse-url-netscape-send (if (>= browse-url-netscape-version 4)
                                "xfeDoCommand(reload)"
-                               "reload")))
+                             "reload")))
 
 (defun browse-url-netscape-send (command)
   "Send a remote control command to Netscape."
 
 (defun browse-url-netscape-send (command)
   "Send a remote control command to Netscape."
@@ -967,11 +990,7 @@ new tab in an existing window instead.
 When called non-interactively, optional second argument NEW-WINDOW is
 used instead of `browse-url-new-window-flag'."
   (interactive (browse-url-interactive-arg "URL: "))
 When called non-interactively, optional second argument NEW-WINDOW is
 used instead of `browse-url-new-window-flag'."
   (interactive (browse-url-interactive-arg "URL: "))
-  ;; URL encode any `confusing' characters in the URL.  This needs to
-  ;; include at least commas; presumably also close parens and dollars.
-  (while (string-match "[,)$]" url)
-    (setq url (replace-match
-              (format "%%%x" (string-to-char (match-string 0 url))) t t url)))
+  (setq url (browse-url-encode-url url))
   (let* ((process-environment (browse-url-process-environment))
          (process
          (apply 'start-process
   (let* ((process-environment (browse-url-process-environment))
          (process
          (apply 'start-process
@@ -1029,11 +1048,7 @@ command line parameter.  Therefore, the
 are ignored as well.  Firefox on Windows will always open the requested
 URL in a new window."
   (interactive (browse-url-interactive-arg "URL: "))
 are ignored as well.  Firefox on Windows will always open the requested
 URL in a new window."
   (interactive (browse-url-interactive-arg "URL: "))
-  ;; URL encode any `confusing' characters in the URL.  This needs to
-  ;; include at least commas; presumably also close parens.
-  (while (string-match "[,)]" url)
-    (setq url (replace-match
-              (format "%%%x" (string-to-char (match-string 0 url))) t t url)))
+  (setq url (browse-url-encode-url url))
   (let* ((process-environment (browse-url-process-environment))
         (process
          (apply 'start-process
   (let* ((process-environment (browse-url-process-environment))
         (process
          (apply 'start-process
@@ -1085,11 +1100,7 @@ new tab in an existing window instead.
 When called non-interactively, optional second argument NEW-WINDOW is
 used instead of `browse-url-new-window-flag'."
   (interactive (browse-url-interactive-arg "URL: "))
 When called non-interactively, optional second argument NEW-WINDOW is
 used instead of `browse-url-new-window-flag'."
   (interactive (browse-url-interactive-arg "URL: "))
-  ;; URL encode any `confusing' characters in the URL.  This needs to
-  ;; include at least commas; presumably also close parens and dollars.
-  (while (string-match "[,)$]" url)
-    (setq url (replace-match
-              (format "%%%x" (string-to-char (match-string 0 url))) t t url)))
+  (setq url (browse-url-encode-url url))
   (let* ((process-environment (browse-url-process-environment))
          (process (apply 'start-process
                         (concat "galeon " url)
   (let* ((process-environment (browse-url-process-environment))
          (process (apply 'start-process
                         (concat "galeon " url)
@@ -1134,11 +1145,7 @@ new tab in an existing window instead.
 When called non-interactively, optional second argument NEW-WINDOW is
 used instead of `browse-url-new-window-flag'."
   (interactive (browse-url-interactive-arg "URL: "))
 When called non-interactively, optional second argument NEW-WINDOW is
 used instead of `browse-url-new-window-flag'."
   (interactive (browse-url-interactive-arg "URL: "))
-  ;; URL encode any `confusing' characters in the URL.  This needs to
-  ;; include at least commas; presumably also close parens and dollars.
-  (while (string-match "[,)$]" url)
-    (setq url (replace-match
-              (format "%%%x" (string-to-char (match-string 0 url))) t t url)))
+  (setq url (browse-url-encode-url url))
   (let* ((process-environment (browse-url-process-environment))
          (process (apply 'start-process
                         (concat "epiphany " url)
   (let* ((process-environment (browse-url-process-environment))
          (process (apply 'start-process
                         (concat "epiphany " url)
@@ -1166,6 +1173,8 @@ used instead of `browse-url-new-window-flag'."
               browse-url-epiphany-program
               (append browse-url-epiphany-startup-arguments (list url))))))
 
               browse-url-epiphany-program
               (append browse-url-epiphany-startup-arguments (list url))))))
 
+(defvar url-handler-regexp)
+
 ;;;###autoload
 (defun browse-url-emacs (url &optional new-window)
   "Ask Emacs to load URL into a buffer and show it in another window."
 ;;;###autoload
 (defun browse-url-emacs (url &optional new-window)
   "Ask Emacs to load URL into a buffer and show it in another window."
@@ -1200,7 +1209,7 @@ used instead of `browse-url-new-window-flag'."
         (append
          browse-url-gnome-moz-arguments
          (if (browse-url-maybe-new-window new-window)
         (append
          browse-url-gnome-moz-arguments
          (if (browse-url-maybe-new-window new-window)
-           '("--newwin"))
+             '("--newwin"))
          (list "--raise" url))))
 
 ;; --- Mosaic ---
          (list "--raise" url))))
 
 ;; --- Mosaic ---
@@ -1253,28 +1262,6 @@ used instead of `browse-url-new-window-flag'."
             (append browse-url-mosaic-arguments (list url)))
       (message "Starting %s...done" browse-url-mosaic-program))))
 
             (append browse-url-mosaic-arguments (list url)))
       (message "Starting %s...done" browse-url-mosaic-program))))
 
-;; --- Grail ---
-
-(defvar browse-url-grail
-  (concat (or (getenv "GRAILDIR") "~/.grail") "/user/rcgrail.py")
-  "Location of Grail remote control client script `rcgrail.py'.
-Typically found in $GRAILDIR/rcgrail.py, or ~/.grail/user/rcgrail.py.")
-
-;;;###autoload
-(defun browse-url-grail (url &optional new-window)
-  "Ask the Grail WWW browser to load URL.
-Default to the URL around or before point.  Runs the program in the
-variable `browse-url-grail'."
-  (interactive (browse-url-interactive-arg "Grail URL: "))
-  (message "Sending URL to Grail...")
-  (with-current-buffer (get-buffer-create " *Shell Command Output*")
-    (erase-buffer)
-    ;; don't worry about this failing.
-    (if (browse-url-maybe-new-window new-window)
-       (call-process browse-url-grail nil 0 nil "-b" url)
-      (call-process browse-url-grail nil 0 nil url))
-    (message "Sending URL to Grail... done")))
-
 ;; --- Mosaic using CCI ---
 
 ;;;###autoload
 ;; --- Mosaic using CCI ---
 
 ;;;###autoload
@@ -1306,19 +1293,12 @@ used instead of `browse-url-new-window-flag'."
   (process-send-string "browse-url" "disconnect\r\n")
   (delete-process "browse-url"))
 
   (process-send-string "browse-url" "disconnect\r\n")
   (delete-process "browse-url"))
 
-;; --- IXI Mosaic ---
-
-;;;###autoload
-(defun browse-url-iximosaic (url &optional new-window)
-  ;; new-window ignored
-  "Ask the IXIMosaic WWW browser to load URL.
-Default to the URL around or before point."
-  (interactive (browse-url-interactive-arg "IXI Mosaic URL: "))
-  (start-process "tellw3b" nil "tellw3b"
-                "-service WWW_BROWSER ixi_showurl " url))
-
 ;; --- W3 ---
 
 ;; --- W3 ---
 
+;; External.
+(declare-function w3-fetch-other-window "ext:w3m" (&optional url))
+(declare-function w3-fetch              "ext:w3m" (&optional url target))
+
 ;;;###autoload
 (defun browse-url-w3 (url &optional new-window)
   "Ask the w3 WWW browser to load URL.
 ;;;###autoload
 (defun browse-url-w3 (url &optional new-window)
   "Ask the w3 WWW browser to load URL.
@@ -1331,7 +1311,7 @@ prefix argument reverses the effect of `browse-url-new-window-flag'.
 When called non-interactively, optional second argument NEW-WINDOW is
 used instead of `browse-url-new-window-flag'."
   (interactive (browse-url-interactive-arg "W3 URL: "))
 When called non-interactively, optional second argument NEW-WINDOW is
 used instead of `browse-url-new-window-flag'."
   (interactive (browse-url-interactive-arg "W3 URL: "))
-  (require 'w3)                                ; w3-fetch-other-window not autoloaded
+  (require 'w3)                        ; w3-fetch-other-window not autoloaded
   (if (browse-url-maybe-new-window new-window)
       (w3-fetch-other-window url)
     (w3-fetch url)))
   (if (browse-url-maybe-new-window new-window)
       (w3-fetch-other-window url)
     (w3-fetch url)))
@@ -1343,47 +1323,50 @@ used instead of `browse-url-new-window-flag'."
 The `browse-url-gnudoit-program' program is used with options given by
 `browse-url-gnudoit-args'.  Default to the URL around or before point."
   (interactive (browse-url-interactive-arg "W3 URL: "))
 The `browse-url-gnudoit-program' program is used with options given by
 `browse-url-gnudoit-args'.  Default to the URL around or before point."
   (interactive (browse-url-interactive-arg "W3 URL: "))
-    (apply 'start-process (concat "gnudoit:" url) nil
-          browse-url-gnudoit-program
-          (append browse-url-gnudoit-args
-                  (list (concat "(w3-fetch \"" url "\")")
-                        "(raise-frame)"))))
+  (apply 'start-process (concat "gnudoit:" url) nil
+        browse-url-gnudoit-program
+        (append browse-url-gnudoit-args
+                (list (concat "(w3-fetch \"" url "\")")
+                      "(raise-frame)"))))
 
 ;; --- Lynx in an xterm ---
 
 ;;;###autoload
 
 ;; --- Lynx in an xterm ---
 
 ;;;###autoload
-(defun browse-url-lynx-xterm (url &optional new-window)
+(defun browse-url-text-xterm (url &optional new-window)
   ;; new-window ignored
   ;; new-window ignored
-  "Ask the Lynx WWW browser to load URL.
-Default to the URL around or before point.  A new Lynx process is run
+  "Ask a text browser to load URL.
+URL defaults to the URL around or before point.
+This runs the text browser specified by `browse-url-text-browser'.
 in an Xterm window using the Xterm program named by `browse-url-xterm-program'
 with possible additional arguments `browse-url-xterm-args'."
 in an Xterm window using the Xterm program named by `browse-url-xterm-program'
 with possible additional arguments `browse-url-xterm-args'."
-  (interactive (browse-url-interactive-arg "Lynx URL: "))
-  (apply #'start-process `(,(concat "lynx" url) nil ,browse-url-xterm-program
-             ,@browse-url-xterm-args "-e" "lynx"
-            ,url)))
+  (interactive (browse-url-interactive-arg "Text browser URL: "))
+  (apply #'start-process `(,(concat browse-url-text-browser url)
+                          nil ,browse-url-xterm-program
+                          ,@browse-url-xterm-args "-e" ,browse-url-text-browser
+                          ,url)))
 
 ;; --- Lynx in an Emacs "term" window ---
 
 ;;;###autoload
 
 ;; --- Lynx in an Emacs "term" window ---
 
 ;;;###autoload
-(defun browse-url-lynx-emacs (url &optional new-buffer)
-  "Ask the Lynx WWW browser to load URL.
-Default to the URL around or before point.  With a prefix argument, run
-a new Lynx process in a new buffer.
+(defun browse-url-text-emacs (url &optional new-buffer)
+  "Ask a text browser to load URL.
+URL defaults to the URL around or before point.
+This runs the text browser specified by `browse-url-text-browser'.
+With a prefix argument, it runs a new browser process in a new buffer.
 
 When called interactively, if variable `browse-url-new-window-flag' is
 
 When called interactively, if variable `browse-url-new-window-flag' is
-non-nil, load the document in a new lynx in a new term window,
+non-nil, load the document in a new browser process in a new term window,
 otherwise use any existing one.  A non-nil interactive prefix argument
 reverses the effect of `browse-url-new-window-flag'.
 
 When called non-interactively, optional second argument NEW-WINDOW is
 used instead of `browse-url-new-window-flag'."
 otherwise use any existing one.  A non-nil interactive prefix argument
 reverses the effect of `browse-url-new-window-flag'.
 
 When called non-interactively, optional second argument NEW-WINDOW is
 used instead of `browse-url-new-window-flag'."
-  (interactive (browse-url-interactive-arg "Lynx URL: "))
-  (let* ((system-uses-terminfo t)       ; Lynx uses terminfo
+  (interactive (browse-url-interactive-arg "Text browser URL: "))
+  (let* ((system-uses-terminfo t)     ; Lynx uses terminfo
         ;; (term-term-name "vt100") ; ??
         ;; (term-term-name "vt100") ; ??
-        (buf (get-buffer "*lynx*"))
+        (buf (get-buffer "*text browser*"))
         (proc (and buf (get-buffer-process buf)))
         (proc (and buf (get-buffer-process buf)))
-        (n browse-url-lynx-input-attempts))
+        (n browse-url-text-input-attempts))
     (if (and (browse-url-maybe-new-window new-buffer) buf)
        ;; Rename away the OLD buffer. This isn't very polite, but
        ;; term insists on working in a buffer named *lynx* and would
     (if (and (browse-url-maybe-new-window new-buffer) buf)
        ;; Rename away the OLD buffer. This isn't very polite, but
        ;; term insists on working in a buffer named *lynx* and would
@@ -1394,11 +1377,13 @@ used instead of `browse-url-new-window-flag'."
            (not buf)
            (not proc)
            (not (memq (process-status proc) '(run stop))))
            (not buf)
            (not proc)
            (not (memq (process-status proc) '(run stop))))
-       ;; start a new lynx
+       ;; start a new text browser
        (progn
           (setq buf
                 (apply #'make-term
        (progn
           (setq buf
                 (apply #'make-term
-                       `("lynx" "lynx" nil ,@browse-url-lynx-emacs-args
+                       `(,browse-url-text-browser
+                        ,browse-url-text-browser
+                        nil ,@browse-url-text-emacs-args
                         ,url)))
           (switch-to-buffer buf)
           (term-char-mode)
                         ,url)))
           (switch-to-buffer buf)
           (term-char-mode)
@@ -1410,43 +1395,25 @@ used instead of `browse-url-new-window-flag'."
              (if (not (memq (process-status process) '(run stop)))
                  (let ((buf (process-buffer process)))
                    (if buf (kill-buffer buf)))))))
              (if (not (memq (process-status process) '(run stop)))
                  (let ((buf (process-buffer process)))
                    (if buf (kill-buffer buf)))))))
-      ;; send the url to lynx in the old buffer
+      ;; Send the url to the text browser in the old buffer
       (let ((win (get-buffer-window buf t)))
        (if win
            (select-window win)
          (switch-to-buffer buf)))
       (if (eq (following-char) ?_)
       (let ((win (get-buffer-window buf t)))
        (if win
            (select-window win)
          (switch-to-buffer buf)))
       (if (eq (following-char) ?_)
-         (cond ((eq browse-url-lynx-input-field 'warn)
+         (cond ((eq browse-url-text-input-field 'warn)
                 (error "Please move out of the input field first"))
                 (error "Please move out of the input field first"))
-               ((eq browse-url-lynx-input-field 'avoid)
+               ((eq browse-url-text-input-field 'avoid)
                 (while (and (eq (following-char) ?_) (> n 0))
                 (while (and (eq (following-char) ?_) (> n 0))
-                  (term-send-down) ; down arrow
-                  (sit-for browse-url-lynx-input-delay))
+                  (term-send-down)     ; down arrow
+                  (sit-for browse-url-text-input-delay))
                 (if (eq (following-char) ?_)
                     (error "Cannot move out of the input field, sorry")))))
                 (if (eq (following-char) ?_)
                     (error "Cannot move out of the input field, sorry")))))
-      (term-send-string proc (concat "g" ; goto
+      (term-send-string proc (concat "g"    ; goto
                                     "\C-u" ; kill default url
                                     url
                                     "\r")))))
 
                                     "\C-u" ; kill default url
                                     url
                                     "\r")))))
 
-;; --- MMM ---
-
-;;;###autoload
-(defun browse-url-mmm (url &optional new-window)
-  "Ask the MMM WWW browser to load URL.
-Default to the URL around or before point."
-  (interactive (browse-url-interactive-arg "MMM URL: "))
-  (message "Sending URL to MMM...")
-  (with-current-buffer (get-buffer-create " *Shell Command Output*")
-    (erase-buffer)
-    ;; mmm_remote just SEGVs if the file isn't there...
-    (if (or (file-exists-p (expand-file-name "~/.mmm_remote"))
-           ;; location in v 0.4:
-           (file-exists-p (expand-file-name "~/.mmm/remote")))
-       (call-process "mmm_remote" nil 0 nil url)
-      (call-process "mmm" nil 0 nil "-external" url))
-    (message "Sending URL to MMM... done")))
-
 ;; --- mailto ---
 
 (autoload 'rfc2368-parse-mailto-url "rfc2368")
 ;; --- mailto ---
 
 (autoload 'rfc2368-parse-mailto-url "rfc2368")
@@ -1499,7 +1466,7 @@ browser is started up in a new process with possible additional arguments
 don't offer a form of remote control."
   (interactive (browse-url-interactive-arg "URL: "))
   (if (not browse-url-generic-program)
 don't offer a form of remote control."
   (interactive (browse-url-interactive-arg "URL: "))
   (if (not browse-url-generic-program)
-    (error "No browser defined (`browse-url-generic-program')"))
+      (error "No browser defined (`browse-url-generic-program')"))
   (apply 'call-process browse-url-generic-program nil
         0 nil
         (append browse-url-generic-args (list url))))
   (apply 'call-process browse-url-generic-program nil
         0 nil
         (append browse-url-generic-args (list url))))
@@ -1511,7 +1478,56 @@ Default to the URL around or before point."
   (interactive (browse-url-interactive-arg "KDE URL: "))
   (message "Sending URL to KDE...")
   (apply #'start-process (concat "KDE " url) nil browse-url-kde-program
   (interactive (browse-url-interactive-arg "KDE URL: "))
   (message "Sending URL to KDE...")
   (apply #'start-process (concat "KDE " url) nil browse-url-kde-program
-                        (append browse-url-kde-args (list url))))
+        (append browse-url-kde-args (list url))))
+
+(defun browse-url-elinks-new-window (url)
+  "Ask the Elinks WWW browser to load URL in a new window."
+  (let ((process-environment (browse-url-process-environment)))
+    (apply #'start-process
+          (append (list (concat "elinks:" url)
+                        nil)
+                  browse-url-elinks-wrapper
+                  (list "elinks" url)))))
+
+;;;###autoload
+(defun browse-url-elinks (url &optional new-window)
+  "Ask the Elinks WWW browser to load URL.
+Default to the URL around the point.
+
+The document is loaded in a new tab of a running Elinks or, if
+none yet running, a newly started instance.
+
+The Elinks command will be prepended by the program+arguments
+from `browse-url-elinks-wrapper'."
+  (interactive (browse-url-interactive-arg "URL: "))
+  (setq url (browse-url-encode-url url))
+  (if new-window
+      (browse-url-elinks-new-window url)
+    (let ((process-environment (browse-url-process-environment))
+         (elinks-ping-process (start-process "elinks-ping" nil
+                                             "elinks" "-remote" "ping()")))
+      (set-process-sentinel elinks-ping-process
+                           `(lambda (process change)
+                              (browse-url-elinks-sentinel process ,url))))))
+
+(defun browse-url-elinks-sentinel (process url)
+  "Determines if Elinks is running or a new one has to be started."
+  (let ((exit-status (process-exit-status process)))
+    ;; Try to determine if an instance is running or if we have to
+    ;; create a new one.
+    (case exit-status
+         (5
+          ;; No instance, start a new one.
+          (browse-url-elinks-new-window url))
+         (0
+          ;; Found an instance, open URL in new tab.
+          (let ((process-environment (browse-url-process-environment)))
+            (start-process (concat "elinks:" url) nil
+                           "elinks" "-remote"
+                           (concat "openURL(\"" url "\",new-tab)"))))
+         (otherwise
+          (error "Unrecognized exit-code %d of process `elinks'"
+                 exit-status)))))
 
 (provide 'browse-url)
 
 
 (provide 'browse-url)