lisp/desktop.el: More fixes to frame restoration.
[bpt/emacs.git] / lisp / ffap.el
index 3eb21a5..0769469 100644 (file)
@@ -1,7 +1,6 @@
 ;;; ffap.el --- find file (or url) at point
 
-;; Copyright (C) 1995, 1996, 1997, 2000, 2001, 2002, 2003, 2004, 2005,
-;;   2006, 2007, 2008, 2009  Free Software Foundation, Inc.
+;; Copyright (C) 1995-1997, 2000-2013 Free Software Foundation, Inc.
 
 ;; Author: Michelangelo Grigni <mic@mathcs.emory.edu>
 ;; Maintainer: FSF
@@ -35,7 +34,7 @@
 ;; README's, MANIFEST's, and so on.  Submit bugs or suggestions with
 ;; M-x ffap-bug.
 ;;
-;; For the default installation, add this line to your .emacs file:
+;; For the default installation, add this line to your init file:
 ;;
 ;; (ffap-bindings)                      ; do default key bindings
 ;;
 \f
 ;;; Code:
 
+(require 'url-parse)
+(require 'thingatpt)
+
 (define-obsolete-variable-alias 'ffap-version 'emacs-version "23.2")
 
 (defgroup ffap nil
 \f
 ;;; User Variables:
 
-(defun ffap-soft-value (name &optional default)
-  "Return value of symbol with NAME, if it is interned.
-Otherwise return nil (or the optional DEFAULT value)."
-  ;; Bug: (ffap-soft-value "nil" 5) --> 5
-  (let ((sym (intern-soft name)))
-    (if (and sym (boundp sym)) (symbol-value sym) default)))
+(defun ffap-symbol-value (sym &optional default)
+  "Return value of symbol SYM, if bound, or DEFAULT otherwise."
+  (if (boundp sym) (symbol-value sym) default))
 
 (defcustom ffap-shell-prompt-regexp
   ;; This used to test for some shell prompts that don't have a space
   ;; after them. The common root shell prompt (#) is not listed since it
   ;; also doubles up as a valid URL character.
   "[$%><]*"
-  "Paths matching this regexp are stripped off the shell prompt
+  "Paths matching this regexp are stripped off the shell prompt.
 If nil, ffap doesn't do shell prompt stripping."
   :type '(choice (const :tag "Disable" nil)
                  (const :tag "Standard" "[$%><]*")
                   regexp)
   :group 'ffap)
 
-(defcustom ffap-ftp-regexp
-  ;; This used to test for ange-ftp or efs being present, but it should be
-  ;; harmless (and simpler) to give it this value unconditionally.
-  "\\`/[^/:]+:"
+(defcustom ffap-ftp-regexp "\\`/[^/:]+:"
   "File names matching this regexp are treated as remote ffap.
 If nil, ffap neither recognizes nor generates such names."
   :type '(choice (const :tag "Disable" nil)
@@ -152,15 +148,20 @@ If nil, ffap neither recognizes nor generates such names."
   :group 'ffap)
 
 (defcustom ffap-url-unwrap-local t
-  "If non-nil, convert `file:' URL to local file name before prompting."
+  "If non-nil, convert some URLs to local file names before prompting.
+Only \"file:\" and \"ftp:\" URLs are converted, and only if they
+do not specify a host, or the host is either \"localhost\" or
+equal to `system-name'."
   :type 'boolean
   :group 'ffap)
 
-(defcustom ffap-url-unwrap-remote t
-  "If non-nil, convert `ftp:' URL to remote file name before prompting.
-This is ignored if `ffap-ftp-regexp' is nil."
-  :type 'boolean
-  :group 'ffap)
+(defcustom ffap-url-unwrap-remote '("ftp")
+  "If non-nil, convert URLs to remote file names before prompting.
+If the value is a list of strings, that specifies a list of URL
+schemes (e.g. \"ftp\"); in that case, only convert those URLs."
+  :type '(choice (repeat string) boolean)
+  :group 'ffap
+  :version "24.3")
 
 (defcustom ffap-ftp-default-user "anonymous"
   "User name in ftp file names generated by `ffap-host-to-path'.
@@ -178,16 +179,14 @@ Note this name may be omitted if it equals the default
   :group 'ffap)
 
 (defvar ffap-url-regexp
-  ;; Could just use `url-nonrelative-link' of w3, if loaded.
-  ;; This regexp is not exhaustive, it just matches common cases.
   (concat
-   "\\`\\("
+   "\\("
    "news\\(post\\)?:\\|mailto:\\|file:" ; no host ok
    "\\|"
    "\\(ftp\\|https?\\|telnet\\|gopher\\|www\\|wais\\)://" ; needs host
-   "\\)."                              ; require one more character
-   )
-   "Regexp matching URL's.  nil to disable URL features in ffap.")
+   "\\)")
+  "Regexp matching the beginning of a URI, for FFAP.
+If the value is nil, disable URL-matching features in ffap.")
 
 (defcustom ffap-foo-at-bar-prefix "mailto"
   "Presumed URL prefix type of strings like \"<foo.9z@bar>\".
@@ -206,7 +205,7 @@ Sensible values are nil, \"news\", or \"mailto\"."
 ;; those features interesting but not clear winners (a matter of
 ;; personal taste) I try to leave options to enable them.  Read
 ;; through this section for features that you like, put an appropriate
-;; enabler in your .emacs file.
+;; enabler in your init file.
 
 (defcustom ffap-dired-wildcards "[*?][^/]*\\'"
   "A regexp matching filename wildcard characters, or nil.
@@ -251,14 +250,14 @@ ffap most of the time."
 (defcustom ffap-file-finder 'find-file
   "The command called by `find-file-at-point' to find a file."
   :type 'function
-  :group 'ffap)
-(put 'ffap-file-finder 'risky-local-variable t)
+  :group 'ffap
+  :risky t)
 
 (defcustom ffap-directory-finder 'dired
   "The command called by `dired-at-point' to find a directory."
   :type 'function
-  :group 'ffap)
-(put 'ffap-directory-finder 'risky-local-variable t)
+  :group 'ffap
+  :risky t)
 
 (defcustom ffap-url-fetcher
   (if (fboundp 'browse-url)
@@ -275,8 +274,28 @@ For a fancy alternative, get `ffap-url.el'."
                 (const browse-url-netscape)
                 (const browse-url-mosaic)
                 function)
+  :group 'ffap
+  :risky t)
+
+(defcustom ffap-next-regexp
+  ;; If you want ffap-next to find URL's only, try this:
+  ;; (and ffap-url-regexp (string-match "\\\\`" ffap-url-regexp)
+  ;;     (concat "\\<" (substring ffap-url-regexp 2))))
+  ;;
+  ;; It pays to put a big fancy regexp here, since ffap-guesser is
+  ;; much more time-consuming than regexp searching:
+  "[/:.~[:alpha:]]/\\|@[[:alpha:]][-[:alnum:]]*\\."
+  "Regular expression governing movements of `ffap-next'."
+  :type 'regexp
   :group 'ffap)
-(put 'ffap-url-fetcher 'risky-local-variable t)
+
+(defcustom dired-at-point-require-prefix nil
+  "If non-nil, reverse the prefix argument to `dired-at-point'.
+This is nil so neophytes notice FFAP.  Experts may prefer to
+disable FFAP most of the time."
+  :type 'boolean
+  :group 'ffap
+  :version "20.3")
 
 \f
 ;;; Compatibility:
@@ -285,7 +304,7 @@ For a fancy alternative, get `ffap-url.el'."
 ;; See the ftp site for a more general version.  The following
 ;; functions are necessary "leftovers" from the more general version.
 
-(defun ffap-mouse-event nil            ; current mouse event, or nil
+(defun ffap-mouse-event ()             ; current mouse event, or nil
   (and (listp last-nonmenu-event) last-nonmenu-event))
 (defun ffap-event-buffer (event)
   (window-buffer (car (event-start event))))
@@ -297,23 +316,11 @@ For a fancy alternative, get `ffap-url.el'."
 ;; then, broke it up into ffap-next-guess (noninteractive) and
 ;; ffap-next (a command).  It now work on files as well as url's.
 
-(defcustom ffap-next-regexp
-  ;; If you want ffap-next to find URL's only, try this:
-  ;; (and ffap-url-regexp (string-match "\\\\`" ffap-url-regexp)
-  ;;     (concat "\\<" (substring ffap-url-regexp 2))))
-  ;;
-  ;; It pays to put a big fancy regexp here, since ffap-guesser is
-  ;; much more time-consuming than regexp searching:
-  "[/:.~[:alpha:]]/\\|@[[:alpha:]][-[:alnum:]]*\\."
-  "Regular expression governing movements of `ffap-next'."
-  :type 'regexp
-  :group 'ffap)
-
 (defvar ffap-next-guess nil
   "Last value returned by `ffap-next-guess'.")
 
 (defvar ffap-string-at-point-region '(1 1)
-  "List (BEG END), last region returned by `ffap-string-at-point'.")
+  "List (BEG END), last region returned by the function `ffap-string-at-point'.")
 
 (defun ffap-next-guess (&optional back lim)
   "Move point to next file or URL, and return it as a string.
@@ -338,7 +345,7 @@ Optional argument BACK says to search backwards.
 Optional argument WRAP says to try wrapping around if necessary.
 Interactively: use a single prefix to search backwards,
 double prefix to wrap forward, triple to wrap backwards.
-Actual search is done by `ffap-next-guess'."
+Actual search is done by the function `ffap-next-guess'."
   (interactive
    (cdr (assq (prefix-numeric-value current-prefix-arg)
              '((1) (4 t) (16 nil t) (64 t t)))))
@@ -360,7 +367,7 @@ Actual search is done by `ffap-next-guess'."
   "Like `ffap-next', but search with `ffap-url-regexp'."
   (interactive)
   (let ((ffap-next-regexp ffap-url-regexp))
-    (if (interactive-p)
+    (if (called-interactively-p 'interactive)
        (call-interactively 'ffap-next)
       (ffap-next back wrap))))
 
@@ -397,8 +404,8 @@ See `mail-extr.el' for the known domains."
 (defun ffap-what-domain (domain)
   ;; Like what-domain in mail-extr.el, returns string or nil.
   (require 'mail-extr)
-  (let ((ob (or (ffap-soft-value "mail-extr-all-top-level-domains")
-               (ffap-soft-value "all-top-level-domains")))) ; XEmacs
+  (let ((ob (or (ffap-symbol-value 'mail-extr-all-top-level-domains)
+               (ffap-symbol-value 'all-top-level-domains)))) ; XEmacs
     (and ob (get (intern-soft (downcase domain) ob) 'domain-name))))
 
 (defun ffap-machine-p (host &optional service quiet strategy)
@@ -474,18 +481,12 @@ Returned values:
 
 (defun ffap-replace-file-component (fullname name)
   "In remote FULLNAME, replace path with NAME.  May return nil."
-  ;; Use ange-ftp or efs if loaded, but do not load them otherwise.
-  (let (found)
-    (mapc
-     (function (lambda (sym) (and (fboundp sym) (setq found sym))))
-     '(
-       efs-replace-path-component
-       ange-ftp-replace-path-component
-       ange-ftp-replace-name-component
-       ))
-    (and found
-        (fset 'ffap-replace-file-component found)
-        (funcall found fullname name))))
+  ;; Use efs if loaded, but do not load it otherwise.
+  (if (fboundp 'efs-replace-path-component)
+      (funcall 'efs-replace-path-component fullname name)
+    (and (stringp fullname)
+        (stringp name)
+        (concat (file-remote-p fullname) name))))
 ;; (ffap-replace-file-component "/who@foo.com:/whatever" "/new")
 
 (defun ffap-file-suffix (file)
@@ -527,7 +528,7 @@ The optional NOMODIFY argument suppresses the extra search."
   ;; (ffap-file-remote-p "/ffap.el:80")
   (or (and ffap-ftp-regexp
           (string-match ffap-ftp-regexp filename)
-          ;; Convert "/host.com://dir" to "/host:/dir", to handle a dieing
+          ;; Convert "/host.com://dir" to "/host:/dir", to handle a dying
           ;; practice of advertising ftp files as "host.dom://filename".
           (if (string-match "//" filename)
               ;; (replace-match "/" nil nil filename)
@@ -538,7 +539,7 @@ The optional NOMODIFY argument suppresses the extra search."
           (string-match ffap-rfs-regexp filename)
           filename)))
 
-(defun ffap-machine-at-point nil
+(defun ffap-machine-at-point ()
   "Return machine name at point if it exists, or nil."
   (let ((mach (ffap-string-at-point 'machine)))
     (and (ffap-machine-p mach) mach)))
@@ -550,8 +551,8 @@ Looks at `ffap-ftp-default-user', returns \"\" for \"localhost\"."
       ""
     (let ((user ffap-ftp-default-user))
       ;; Avoid including the user if it is same as default:
-      (if (or (equal user (ffap-soft-value "ange-ftp-default-user"))
-             (equal user (ffap-soft-value "efs-default-user")))
+      (if (or (equal user (ffap-symbol-value 'ange-ftp-default-user))
+             (equal user (ffap-symbol-value 'efs-default-user)))
          (setq user nil))
       (concat "/" user (and user "@") host ":"))))
 
@@ -569,69 +570,58 @@ Looks at `ffap-ftp-default-user', returns \"\" for \"localhost\"."
    (ffap-ftp-regexp (ffap-host-to-filename mach))
    ))
 
-(defvar ffap-newsgroup-regexp "^[[:lower:]]+\\.[-+[:lower:]_0-9.]+$"
-  "Strings not matching this fail `ffap-newsgroup-p'.")
-(defvar ffap-newsgroup-heads           ; entirely inadequate
-  '("alt" "comp" "gnu" "misc" "news" "sci" "soc" "talk")
-  "Used by `ffap-newsgroup-p' if gnus is not running.")
-
-(defun ffap-newsgroup-p (string)
-  "Return STRING if it looks like a newsgroup name, else nil."
-  (and
-   (string-match ffap-newsgroup-regexp string)
-   (let ((htbs '(gnus-active-hashtb gnus-newsrc-hashtb gnus-killed-hashtb))
-        (heads ffap-newsgroup-heads)
-        htb ret)
-     (while htbs
-       (setq htb (car htbs) htbs (cdr htbs))
-       (condition-case nil
-          (progn
-            ;; errs: htb symbol may be unbound, or not a hash-table.
-            ;; gnus-gethash is just a macro for intern-soft.
-            (and (symbol-value htb)
-                 (intern-soft string (symbol-value htb))
-                 (setq ret string htbs nil))
-            ;; If we made it this far, gnus is running, so ignore "heads":
-            (setq heads nil))
-        (error nil)))
-     (or ret (not heads)
-        (let ((head (string-match "\\`\\([[:lower:]]+\\)\\." string)))
-          (and head (setq head (substring string 0 (match-end 1)))
-               (member head heads)
-               (setq ret string))))
-     ;; Is there ever a need to modify string as a newsgroup name?
-     ret)))
+(defvaralias 'ffap-newsgroup-regexp 'thing-at-point-newsgroup-regexp)
+(defvaralias 'ffap-newsgroup-heads  'thing-at-point-newsgroup-heads)
+(defalias 'ffap-newsgroup-p 'thing-at-point-newsgroup-p)
 
 (defsubst ffap-url-p (string)
-  "If STRING looks like an url, return it (maybe improved), else nil."
-  (let ((case-fold-search t))
-    (and ffap-url-regexp (string-match ffap-url-regexp string)
-        ;; I lied, no improvement:
-        string)))
+  "If STRING looks like an URL, return it (maybe improved), else nil."
+  (when (and (stringp string) ffap-url-regexp)
+    (let* ((case-fold-search t)
+          (match (string-match ffap-url-regexp string)))
+      (cond ((eq match 0) string)
+           (match (substring string match))))))
 
 ;; Broke these out of ffap-fixup-url, for use of ffap-url package.
-(defsubst ffap-url-unwrap-local (url)
-  "Return URL as a local file, or nil.  Ignores `ffap-url-regexp'."
-  (and (string-match "\\`\\(file\\|ftp\\):/?\\([^/]\\|\\'\\)" url)
-       (substring url (1+ (match-end 1)))))
-(defsubst ffap-url-unwrap-remote (url)
-  "Return URL as a remote file, or nil.  Ignores `ffap-url-regexp'."
-  (and (string-match "\\`\\(ftp\\|file\\)://\\([^:/]+\\):?\\(/.*\\)" url)
-       (concat
-       (ffap-host-to-filename (substring url (match-beginning 2) (match-end 2)))
-       (substring url (match-beginning 3) (match-end 3)))))
-;; Test: (ffap-url-unwrap-remote "ftp://foo.com/bar.boz")
+(defun ffap-url-unwrap-local (url)
+  "Return URL as a local file name, or nil."
+  (let* ((obj (url-generic-parse-url url))
+        (host (url-host obj))
+        (filename (car (url-path-and-query obj))))
+    (when (and (member (url-type obj) '("ftp" "file"))
+              (member host `("" "localhost" ,(system-name))))
+      ;; On Windows, "file:///C:/foo" should unwrap to "C:/foo"
+      (if (and (memq system-type '(ms-dos windows-nt cygwin))
+              (string-match "\\`/[a-zA-Z]:" filename))
+         (substring filename 1)
+       filename))))
+
+(defun ffap-url-unwrap-remote (url)
+  "Return URL as a remote file name, or nil."
+  (let* ((obj    (url-generic-parse-url url))
+        (scheme (url-type obj))
+        (valid-schemes (if (listp ffap-url-unwrap-remote)
+                           ffap-url-unwrap-remote
+                         '("ftp")))
+        (host (url-host obj))
+        (port (url-port-if-non-default obj))
+        (user (url-user obj))
+        (filename (car (url-path-and-query obj))))
+    (when (and (member scheme valid-schemes)
+              (string-match "\\`[a-zA-Z][-a-zA-Z0-9+.]*\\'" scheme)
+              (not (equal host "")))
+      (concat "/" scheme ":"
+             (if user (concat user "@"))
+             host
+             (if port (concat "#" (number-to-string port)))
+             ":" filename))))
 
 (defun ffap-fixup-url (url)
   "Clean up URL and return it, maybe as a file name."
   (cond
    ((not (stringp url)) nil)
-   ((and ffap-url-unwrap-local (ffap-url-unwrap-local url)))
-   ((and ffap-url-unwrap-remote ffap-ftp-regexp
-        (ffap-url-unwrap-remote url)))
-   ;; All this seems to do is remove any trailing "#anchor" part (Bug#898).
-;;;   ((fboundp 'url-normalize-url)    ; may autoload url (part of w3)
-;;;    (url-normalize-url url))
+   ((and ffap-url-unwrap-local  (ffap-url-unwrap-local url)))
+   ((and ffap-url-unwrap-remote (ffap-url-unwrap-remote url)))
    (url)))
 
 \f
@@ -684,7 +674,7 @@ Uses `path-separator' to separate the path into substrings."
     (nreverse ret)))
 
 (defun ffap-all-subdirs (dir &optional depth)
-  "Return list all subdirectories under DIR, starting with itself.
+  "Return list of all subdirectories under DIR, starting with itself.
 Directories beginning with \".\" are ignored, and directory symlinks
 are listed but never searched (to avoid loops).
 Optional DEPTH limits search depth."
@@ -724,27 +714,24 @@ kpathsea, a library used by some versions of TeX."
               (list dir))))
          path)))
 
-(defun ffap-locate-file (file &optional nosuffix path dir-ok)
+(defun ffap-locate-file (file nosuffix path)
   ;; The current version of locate-library could almost replace this,
   ;; except it does not let us override the suffix list.  The
   ;; compression-suffixes search moved to ffap-file-exists-string.
-  "A generic path-searching function, mimics `load' by default.
-Returns path to file that \(load FILE\) would load, or nil.
+  "A generic path-searching function.
+Returns the name of file in PATH, or nil.
 Optional NOSUFFIX, if nil or t, is like the fourth argument
-for load: whether to try the suffixes (\".elc\" \".el\" \"\").
+for `load': whether to try the suffixes (\".elc\" \".el\" \"\").
 If a nonempty list, it is a list of suffixes to try instead.
-Optional PATH is a list of directories instead of `load-path'.
-Optional DIR-OK means that returning a directory is allowed,
-DIR-OK is already implicit if FILE looks like a directory.
+PATH is a list of directories.
 
-This uses ffap-file-exists-string, which may try adding suffixes from
+This uses `ffap-file-exists-string', which may try adding suffixes from
 `ffap-compression-suffixes'."
-  (or path (setq path load-path))
-  (or dir-ok (setq dir-ok (equal "" (file-name-nondirectory file))))
   (if (file-name-absolute-p file)
       (setq path (list (file-name-directory file))
            file (file-name-nondirectory file)))
-  (let ((suffixes-to-try
+  (let ((dir-ok (equal "" (file-name-nondirectory file)))
+        (suffixes-to-try
         (cond
          ((consp nosuffix) nosuffix)
          (nosuffix '(""))
@@ -792,20 +779,20 @@ This uses ffap-file-exists-string, which may try adding suffixes from
     ("\\.bib\\'" . ffap-bib)           ; search ffap-bib-path
     ("\\`\\." . ffap-home)             ; .emacs, .bashrc, .profile
     ("\\`~/" . ffap-lcd)               ; |~/misc/ffap.el.Z|
-    ;; This uses to have a blank, but ffap-string-at-point doesn't
+    ;; This used to have a blank, but ffap-string-at-point doesn't
     ;; handle blanks.
     ;; http://lists.gnu.org/archive/html/emacs-devel/2008-01/msg01058.html
-    ("^[Rr][Ff][Cc][-#]?\\([0-9]+\\)"  ; no $
+    ("\\`[Rr][Ff][Cc][-#]?\\([0-9]+\\)"        ; no $
      . ffap-rfc)                       ; "100% RFC2100 compliant"
     (dired-mode . ffap-dired)          ; maybe in a subdirectory
     )
   "Alist of \(KEY . FUNCTION\) pairs parsed by `ffap-file-at-point'.
-If string NAME at point (maybe \"\") is not a file or url, these pairs
+If string NAME at point (maybe \"\") is not a file or URL, these pairs
 specify actions to try creating such a string.  A pair matches if either
   KEY is a symbol, and it equals `major-mode', or
-  KEY is a string, it should matches NAME as a regexp.
+  KEY is a string, it should match NAME as a regexp.
 On a match, \(FUNCTION NAME\) is called and should return a file, an
-url, or nil. If nil, search the alist for further matches.")
+URL, or nil.  If nil, search the alist for further matches.")
 
 (put 'ffap-alist 'risky-local-variable t)
 
@@ -839,8 +826,8 @@ url, or nil. If nil, search the alist for further matches.")
 (defun ffap-info (name)
   (ffap-locate-file
    name '("" ".info")
-   (or (ffap-soft-value "Info-directory-list")
-       (ffap-soft-value "Info-default-directory-list")
+   (or (ffap-symbol-value 'Info-directory-list)
+       (ffap-symbol-value 'Info-default-directory-list)
        )))
 
 (defun ffap-info-2 (name) (ffap-info (substring name 5)))
@@ -849,17 +836,32 @@ url, or nil. If nil, search the alist for further matches.")
   ;; This ignores the node! "(emacs)Top" same as "(emacs)Intro"
   (and (equal (ffap-string-around) "()") (ffap-info name)))
 
-(defun ffap-el (name) (ffap-locate-file name t))
+(defun ffap-el (name) (ffap-locate-file name t load-path))
 
 (defun ffap-el-mode (name)
   ;; If name == "foo.el" we will skip it, since ffap-el already
   ;; searched for it once.  (This assumes the default ffap-alist.)
   (and (not (string-match "\\.el\\'" name))
-       (ffap-locate-file name '(".el"))))
+       (ffap-locate-file name '(".el") load-path)))
 
+;; FIXME this duplicates the logic of Man-header-file-path.
+;; There should be a single central variable or function for this.
+;; See also (bug#10702):
+;; cc-search-directories, semantic-c-dependency-system-include-path,
+;; semantic-gcc-setup
 (defvar ffap-c-path
-  ;; Need smarter defaults here!  Suggestions welcome.
-  '("/usr/include" "/usr/local/include"))
+  (let ((arch (with-temp-buffer
+                (when (eq 0 (ignore-errors
+                              (call-process "gcc" nil '(t nil) nil
+                                            "-print-multiarch")))
+                  (goto-char (point-min))
+                  (buffer-substring (point) (line-end-position)))))
+        (base '("/usr/include" "/usr/local/include")))
+    (if (zerop (length arch))
+        base
+      (append base (list (expand-file-name arch "/usr/include")))))
+  "List of directories to search for include files.")
+
 (defun ffap-c-mode (name)
   (ffap-locate-file name t ffap-c-path))
 
@@ -870,10 +872,10 @@ url, or nil. If nil, search the alist for further matches.")
 
 (defvar ffap-tex-path
   t                            ; delayed initialization
-  "Path where `ffap-tex-mode' looks for tex files.
+  "Path where `ffap-tex-mode' looks for TeX files.
 If t, `ffap-tex-init' will initialize this when needed.")
 
-(defun ffap-tex-init nil
+(defun ffap-tex-init ()
   ;; Compute ffap-tex-path if it is now t.
   (and (eq t ffap-tex-path)
        ;; this may be slow, so say something
@@ -886,8 +888,8 @@ If t, `ffap-tex-init' will initialize this when needed.")
                (append
                 (ffap-list-env "TEXINPUTS")
                 ;; (ffap-list-env "BIBINPUTS")
-                (ffap-soft-value
-                 "TeX-macro-global"    ; AUCTeX
+                (ffap-symbol-value
+                 'TeX-macro-global     ; AUCTeX
                  '("/usr/local/lib/tex/macros"
                    "/usr/local/lib/tex/inputs")))))))))
 
@@ -934,6 +936,7 @@ If t, `ffap-tex-init' will initialize this when needed.")
 
 ;; Maybe a "Lisp Code Directory" reference:
 (defun ffap-lcd (name)
+  ;; FIXME: Is this still in use?
   (and
    (or
     ;; lisp-dir-apropos output buffer:
@@ -944,13 +947,23 @@ If t, `ffap-tex-init' will initialize this when needed.")
    (concat
     ;; lispdir.el may not be loaded yet:
     (ffap-host-to-filename
-     (ffap-soft-value "elisp-archive-host"
-                     "archive.cis.ohio-state.edu"))
+     (ffap-symbol-value 'elisp-archive-host
+                        "archive.cis.ohio-state.edu"))
     (file-name-as-directory
-     (ffap-soft-value "elisp-archive-directory"
-                     "/pub/gnu/emacs/elisp-archive/"))
+     (ffap-symbol-value 'elisp-archive-directory
+                        "/pub/gnu/emacs/elisp-archive/"))
     (substring name 2))))
 
+(defcustom ffap-rfc-path
+  (concat (ffap-host-to-filename "ftp.rfc-editor.org") "/in-notes/rfc%s.txt")
+  "A `format' string making a filename for RFC documents.
+This can be an ange-ftp or tramp remote filename to download, or
+a local filename if you have full set of RFCs locally.  See also
+`ffap-rfc-directories'."
+  :type 'string
+  :version "23.1"
+  :group 'ffap)
+
 (defcustom ffap-rfc-directories nil
   "A list of directories to look for RFC files.
 If a given RFC isn't in these then `ffap-rfc-path' is offered."
@@ -958,9 +971,6 @@ If a given RFC isn't in these then `ffap-rfc-path' is offered."
   :version "23.1"
   :group 'ffap)
 
-(defvar ffap-rfc-path
-  (concat (ffap-host-to-filename "ftp.rfc-editor.org") "/in-notes/rfc%s.txt"))
-
 (defun ffap-rfc (name)
   (let ((num (match-string 1 name)))
     (or (ffap-locate-file (format "rfc%s.txt" num) t ffap-rfc-directories)
@@ -977,7 +987,7 @@ If a given RFC isn't in these then `ffap-rfc-path' is offered."
     ;; * no commas (good for latex)
     (file "--:\\\\$+<>@-Z_[:alpha:]~*?" "<@" "@>;.,!:")
     ;; An url, or maybe a email/news message-id:
-    (url "--:=&?$+@-Z_[:alpha:]~#,%;*" "^[:alnum:]" ":;.,!?")
+    (url "--:=&?$+@-Z_[:alpha:]~#,%;*()!'" "^[0-9a-zA-Z]" ":;.,!?")
     ;; Find a string that does *not* contain a colon:
     (nocolon "--9$+<>@-Z_[:alpha:]~" "<@" "@>;.,!?")
     ;; A machine:
@@ -988,14 +998,14 @@ If a given RFC isn't in these then `ffap-rfc-path' is offered."
   "Alist of \(MODE CHARS BEG END\), where MODE is a symbol,
 possibly a major-mode name, or one of the symbol
 `file', `url', `machine', and `nocolon'.
-`ffap-string-at-point' uses the data fields as follows:
+Function `ffap-string-at-point' uses the data fields as follows:
 1. find a maximal string of CHARS around point,
 2. strip BEG chars before point from the beginning,
-3. Strip END chars after point from the end.")
+3. strip END chars after point from the end.")
 
 (defvar ffap-string-at-point nil
   ;; Added at suggestion of RHOGEE (for ff-paths), 7/24/95.
-  "Last string returned by `ffap-string-at-point'.")
+  "Last string returned by the function `ffap-string-at-point'.")
 
 (defun ffap-string-at-point (&optional mode)
   "Return a string of characters from around point.
@@ -1003,32 +1013,34 @@ MODE (defaults to value of `major-mode') is a symbol used to look up string
 syntax parameters in `ffap-string-at-point-mode-alist'.
 If MODE is not found, we use `file' instead of MODE.
 If the region is active, return a string from the region.
-Sets `ffap-string-at-point' and `ffap-string-at-point-region'."
+Sets the variable `ffap-string-at-point' and the variable
+`ffap-string-at-point-region'."
   (let* ((args
          (cdr
           (or (assq (or mode major-mode) ffap-string-at-point-mode-alist)
               (assq 'file ffap-string-at-point-mode-alist))))
         (pt (point))
-        (str
-         (if (and transient-mark-mode mark-active)
-             (buffer-substring
-              (setcar ffap-string-at-point-region (region-beginning))
-              (setcar (cdr ffap-string-at-point-region) (region-end)))
-           (buffer-substring
-            (save-excursion
-              (skip-chars-backward (car args))
-              (skip-chars-forward (nth 1 args) pt)
-              (setcar ffap-string-at-point-region (point)))
-            (save-excursion
-              (skip-chars-forward (car args))
-              (skip-chars-backward (nth 2 args) pt)
-              (setcar (cdr ffap-string-at-point-region) (point)))))))
-    (set-text-properties 0 (length str) nil str)
-    (setq ffap-string-at-point str)))
-
-(defun ffap-string-around nil
+        (beg (if (use-region-p)
+                 (region-beginning)
+               (save-excursion
+                 (skip-chars-backward (car args))
+                 (skip-chars-forward (nth 1 args) pt)
+                 (point))))
+        (end (if (use-region-p)
+                 (region-end)
+               (save-excursion
+                 (skip-chars-forward (car args))
+                 (skip-chars-backward (nth 2 args) pt)
+                 (point)))))
+    (setq ffap-string-at-point
+         (buffer-substring-no-properties
+          (setcar ffap-string-at-point-region beg)
+          (setcar (cdr ffap-string-at-point-region) end)))))
+
+(defun ffap-string-around ()
   ;; Sometimes useful to decide how to treat a string.
-  "Return string of two chars around last `ffap-string-at-point'.
+  "Return string of two chars around last result of function
+`ffap-string-at-point'.
 Assumes the buffer has not changed."
   (save-excursion
     (format "%c%c"
@@ -1042,7 +1054,7 @@ Assumes the buffer has not changed."
 
 (defun ffap-copy-string-as-kill (&optional mode)
   ;; Requested by MCOOK.  Useful?
-  "Call `ffap-string-at-point', and copy result to `kill-ring'."
+  "Call function `ffap-string-at-point', and copy result to `kill-ring'."
   (interactive)
   (let ((str (ffap-string-at-point mode)))
     (if (equal "" str)
@@ -1054,52 +1066,25 @@ Assumes the buffer has not changed."
 ;; External.
 (declare-function w3-view-this-url "ext:w3" (&optional no-show))
 
-(defun ffap-url-at-point nil
-  "Return url from around point if it exists, or nil."
-  ;; Could use w3's url-get-url-at-point instead.  Both handle "URL:",
-  ;; ignore non-relative links, trim punctuation.  The other will
-  ;; actually look back if point is in whitespace, but I would rather
-  ;; ffap be less aggressive in such situations.
-  (and
-   ffap-url-regexp
-   (or
-    ;; In a w3 buffer button?
-    (and (eq major-mode 'w3-mode)
-        ;; interface recommended by wmperry:
-        (w3-view-this-url t))
-    ;; Is there a reason not to strip trailing colon?
-    (let ((name (ffap-string-at-point 'url)))
-      (cond
-       ((string-match "^url:" name) (setq name (substring name 4)))
-       ((and (string-match "\\`[^:</>@]+@[^:</>@]+[[:alnum:]]\\'" name)
-            ;; "foo@bar": could be "mailto" or "news" (a Message-ID).
-            ;; Without "<>" it must be "mailto".  Otherwise could be
-            ;; either, so consult `ffap-foo-at-bar-prefix'.
-            (let ((prefix (if (and (equal (ffap-string-around) "<>")
-                                   ;; Expect some odd characters:
-                                   (string-match "[$.0-9].*[$.0-9].*@" name))
-                              ;; Could be news:
-                              ffap-foo-at-bar-prefix
-                            "mailto")))
-              (and prefix (setq name (concat prefix ":" name))))))
-       ((ffap-newsgroup-p name) (setq name (concat "news:" name)))
-       ((and (string-match "\\`[[:alnum:]]+\\'" name) ; <mic> <root> <nobody>
-            (equal (ffap-string-around) "<>")
-            ;; (ffap-user-p name):
-            (not (string-match "~" (expand-file-name (concat "~" name))))
-            )
-       (setq name (concat "mailto:" name)))
-       )
-      (and (ffap-url-p name) name)
-      ))))
+(defun ffap-url-at-point ()
+  "Return URL from around point if it exists, or nil."
+  (when ffap-url-regexp
+    (or (and (eq major-mode 'w3-mode) ; In a w3 buffer button?
+            (w3-view-this-url t))
+       (let ((thing-at-point-beginning-of-url-regexp ffap-url-regexp)
+             (thing-at-point-default-mail-scheme ffap-foo-at-bar-prefix))
+         (thing-at-point-url-at-point t
+                                      (if (use-region-p)
+                                          (cons (region-beginning)
+                                                (region-end))))))))
 
 (defvar ffap-gopher-regexp
   "^.*\\<\\(Type\\|Name\\|Path\\|Host\\|Port\\) *= *\\(.*\\) *$"
-  "Regexp Matching a line in a gopher bookmark (maybe indented).
+  "Regexp matching a line in a gopher bookmark (maybe indented).
 The two subexpressions are the KEY and VALUE.")
 
-(defun ffap-gopher-at-point nil
-  "If point is inside a gopher bookmark block, return its url."
+(defun ffap-gopher-at-point ()
+  "If point is inside a gopher bookmark block, return its URL."
   ;; `gopher-parse-bookmark' from gopher.el is not so robust
   (save-excursion
     (beginning-of-line)
@@ -1108,7 +1093,7 @@ The two subexpressions are the KEY and VALUE.")
          (while (and (looking-at ffap-gopher-regexp) (not (bobp)))
            (forward-line -1))
          (or (looking-at ffap-gopher-regexp) (forward-line 1))
-         (let ((type "1") name path host (port "70"))
+         (let ((type "1") path host (port "70"))
            (while (looking-at ffap-gopher-regexp)
              (let ((var (intern
                          (downcase
@@ -1138,11 +1123,11 @@ The two subexpressions are the KEY and VALUE.")
   "Strings matching this are coerced to ftp file names by ffap.
 That is, ffap just prepends \"/\".  Set to nil to disable.")
 
-(defun ffap-file-at-point nil
+(defun ffap-file-at-point ()
   "Return filename from around point if it exists, or nil.
 Existence test is skipped for names that look remote.
 If the filename is not obvious, it also tries `ffap-alist',
-which may actually result in an url rather than a filename."
+which may actually result in an URL rather than a filename."
   ;; Note: this function does not need to look for url's, just
   ;; filenames.  On the other hand, it is responsible for converting
   ;; a pseudo-url "site.com://dir" to an ftp file name
@@ -1258,18 +1243,16 @@ which may actually result in an url rather than a filename."
 ;; contents before attempting to complete filenames.
 
 (defun ffap-read-file-or-url (prompt guess)
-  "Read file or url from minibuffer, with PROMPT and initial GUESS."
+  "Read file or URL from minibuffer, with PROMPT and initial GUESS."
   (or guess (setq guess default-directory))
   (let (dir)
     ;; Tricky: guess may have or be a local directory, like "w3/w3.elc"
     ;; or "w3/" or "../el/ffap.el" or "../../../"
-    (or (ffap-url-p guess)
-       (progn
-         (or (ffap-file-remote-p guess)
-             (setq guess
-                   (abbreviate-file-name (expand-file-name guess))
-                   ))
-         (setq dir (file-name-directory guess))))
+    (unless (ffap-url-p guess)
+      (unless (ffap-file-remote-p guess)
+       (setq guess
+             (abbreviate-file-name (expand-file-name guess))))
+      (setq dir (file-name-directory guess)))
     (let ((minibuffer-completing-file-name t)
          (completion-ignore-case read-file-name-completion-ignore-case)
           (fnh-elem (cons ffap-url-regexp 'url-file-handler)))
@@ -1293,15 +1276,12 @@ which may actually result in an url rather than a filename."
         ;; other modifications to be lost (e.g. when Tramp gets loaded
         ;; during the completing-read call).
         (setq file-name-handler-alist (delq fnh-elem file-name-handler-alist))))
-    ;; Do file substitution like (interactive "F"), suggested by MCOOK.
-    (or (ffap-url-p guess) (setq guess (substitute-in-file-name guess)))
-    ;; Should not do it on url's, where $ is a common (VMS?) character.
-    ;; Note: upcoming url.el package ought to handle this automatically.
-    guess))
+    (or (ffap-url-p guess)
+       (substitute-in-file-name guess))))
 
 (defun ffap-read-url-internal (string pred action)
-  "Complete url's from history, treating given string as valid."
-  (let ((hist (ffap-soft-value "url-global-history-hash-table")))
+  "Complete URLs from history, treating given string as valid."
+  (let ((hist (ffap-symbol-value 'url-global-history-hash-table)))
     (cond
      ((not action)
       (or (try-completion string hist pred) string))
@@ -1312,11 +1292,10 @@ which may actually result in an url rather than a filename."
      (t t))))
 
 (defun ffap-read-file-or-url-internal (string pred action)
-  (unless string                        ;Why would this ever happen?
-    (setq string default-directory))
-  (if (ffap-url-p string)
-      (ffap-read-url-internal string pred action)
-    (read-file-name-internal string pred action)))
+  (let ((url (ffap-url-p string)))
+    (if url
+       (ffap-read-url-internal url pred action)
+      (read-file-name-internal (or string default-directory) pred action))))
 
 ;; The rest of this page is just to work with package complete.el.
 ;; This code assumes that you load ffap.el after complete.el.
@@ -1324,24 +1303,8 @@ which may actually result in an url rather than a filename."
 ;; We must inform complete about whether our completion function
 ;; will do filename style completion.
 
-(defun ffap-complete-as-file-p nil
-  ;; Will `minibuffer-completion-table' complete the minibuffer
-  ;; contents as a filename?  Assumes the minibuffer is current.
-  ;; Note: t and non-nil mean somewhat different reasons.
-  (if (eq minibuffer-completion-table 'ffap-read-file-or-url-internal)
-      (not (ffap-url-p (buffer-string))) ; t
-    (and minibuffer-completing-file-name '(t)))) ;list
-
-(and
- (featurep 'complete)
- (if (boundp 'PC-completion-as-file-name-predicate)
-     ;; modern version of complete.el, just set the variable:
-     (setq PC-completion-as-file-name-predicate 'ffap-complete-as-file-p)))
-
 \f
 ;;; Highlighting (`ffap-highlight'):
-;;
-;; Based on overlay highlighting in Emacs 19.28 isearch.el.
 
 (defvar ffap-highlight t
   "If non-nil, ffap highlights the current buffer substring.")
@@ -1353,7 +1316,7 @@ which may actually result in an url rather than a filename."
   :version "22.1")
 
 (defvar ffap-highlight-overlay nil
-  "Overlay used by `ffap-highlight'.")
+  "Overlay used by function `ffap-highlight'.")
 
 (defun ffap-highlight (&optional remove)
   "If `ffap-highlight' is set, highlight the guess in this buffer.
@@ -1380,7 +1343,7 @@ Uses the face `ffap' if it is defined, or else `highlight'."
 \f
 ;;; Main Entrance (`find-file-at-point' == `ffap'):
 
-(defun ffap-guesser nil
+(defun ffap-guesser ()
   "Return file or URL or nil, guessed from text around point."
   (or (and ffap-url-regexp
           (ffap-fixup-url (or (ffap-url-at-point)
@@ -1416,37 +1379,38 @@ If `ffap-require-prefix' is set, the prefix meaning is reversed.
 See also the variables `ffap-dired-wildcards', `ffap-newfile-prompt',
 and the functions `ffap-file-at-point' and `ffap-url-at-point'."
   (interactive)
-  (if (and (interactive-p)
+  (if (and (called-interactively-p 'interactive)
           (if ffap-require-prefix (not current-prefix-arg)
             current-prefix-arg))
       ;; Do exactly the ffap-file-finder command, even the prompting:
       (let (current-prefix-arg)                ; we already interpreted it
        (call-interactively ffap-file-finder))
     (or filename (setq filename (ffap-prompter)))
-    (cond
-     ((ffap-url-p filename)
-      (let (current-prefix-arg)                ; w3 2.3.25 bug, reported by KPC
-       (funcall ffap-url-fetcher filename)))
-     ((and ffap-pass-wildcards-to-dired
-          ffap-dired-wildcards
-          (string-match ffap-dired-wildcards filename))
-      (funcall ffap-directory-finder filename))
-     ((and ffap-dired-wildcards
-          (string-match ffap-dired-wildcards filename)
-          find-file-wildcards
-          ;; Check if it's find-file that supports wildcards arg
-          (memq ffap-file-finder '(find-file find-alternate-file)))
-      (funcall ffap-file-finder (expand-file-name filename) t))
-     ((or (not ffap-newfile-prompt)
-         (file-exists-p filename)
-         (y-or-n-p "File does not exist, create buffer? "))
-      (funcall ffap-file-finder
-              ;; expand-file-name fixes "~/~/.emacs" bug sent by CHUCKR.
-              (expand-file-name filename)))
-     ;; User does not want to find a non-existent file:
-     ((signal 'file-error (list "Opening file buffer"
-                               "no such file or directory"
-                               filename))))))
+    (let ((url (ffap-url-p filename)))
+      (cond
+       (url
+       (let (current-prefix-arg)
+         (funcall ffap-url-fetcher url)))
+       ((and ffap-pass-wildcards-to-dired
+            ffap-dired-wildcards
+            (string-match ffap-dired-wildcards filename))
+       (funcall ffap-directory-finder filename))
+       ((and ffap-dired-wildcards
+            (string-match ffap-dired-wildcards filename)
+            find-file-wildcards
+            ;; Check if it's find-file that supports wildcards arg
+            (memq ffap-file-finder '(find-file find-alternate-file)))
+       (funcall ffap-file-finder (expand-file-name filename) t))
+       ((or (not ffap-newfile-prompt)
+           (file-exists-p filename)
+           (y-or-n-p "File does not exist, create buffer? "))
+       (funcall ffap-file-finder
+                ;; expand-file-name fixes "~/~/.emacs" bug sent by CHUCKR.
+                (expand-file-name filename)))
+       ;; User does not want to find a non-existent file:
+       ((signal 'file-error (list "Opening file buffer"
+                                 "no such file or directory"
+                                 filename)))))))
 
 ;; Shortcut: allow {M-x ffap} rather than {M-x find-file-at-point}.
 ;;;###autoload
@@ -1455,10 +1419,12 @@ and the functions `ffap-file-at-point' and `ffap-url-at-point'."
 \f
 ;;; Menu support (`ffap-menu'):
 
-(defvar ffap-menu-regexp nil
-  "*If non-nil, overrides `ffap-next-regexp' during `ffap-menu'.
+(defcustom ffap-menu-regexp nil
+  "If non-nil, regexp overriding `ffap-next-regexp' in `ffap-menu'.
 Make this more restrictive for faster menu building.
-For example, try \":/\" for URL (and some ftp) references.")
+For example, try \":/\" for URL (and some ftp) references."
+  :type '(choice (const nil) regexp)
+  :group 'ffap)
 
 (defvar ffap-menu-alist nil
   "Buffer local cache of menu presented by `ffap-menu'.")
@@ -1473,7 +1439,7 @@ These properties may be used to fontify the menu references.")
 
 ;;;###autoload
 (defun ffap-menu (&optional rescan)
-  "Put up a menu of files and urls mentioned in this buffer.
+  "Put up a menu of files and URLs mentioned in this buffer.
 Then set mark, jump to choice, and try to fetch it.  The menu is
 cached in `ffap-menu-alist', and rebuilt by `ffap-menu-rescan'.
 The optional RESCAN argument \(a prefix, interactively\) forces
@@ -1545,7 +1511,7 @@ Function CONT is applied to the entry chosen by the user."
       (message "No choice made!")      ; possible with menus
       nil)))
 
-(defun ffap-menu-rescan nil
+(defun ffap-menu-rescan ()
   "Search buffer for `ffap-menu-regexp' to build `ffap-menu-alist'.
 Applies `ffap-menu-text-plist' text properties at all matches."
   (interactive)
@@ -1599,7 +1565,7 @@ Ignored when `ffap-at-mouse' is called programmatically.")
 
 ;;;###autoload
 (defun ffap-at-mouse (e)
-  "Find file or url guessed from text around mouse click.
+  "Find file or URL guessed from text around mouse click.
 Interactively, calls `ffap-at-mouse-fallback' if no guess is found.
 Return value:
   * if a guess string is found, return it (after finding it)
@@ -1624,10 +1590,10 @@ Return value:
            (find-file-at-point guess)
            guess)                      ; success: return non-nil
        (ffap-highlight t)))
-     ((interactive-p)
+     ((called-interactively-p 'interactive)
       (if ffap-at-mouse-fallback
          (call-interactively ffap-at-mouse-fallback)
-       (message "No file or url found at mouse click.")
+       (message "No file or URL found at mouse click.")
        nil))                           ; no fallback, return nil
      ;; failure: return nil
      )))
@@ -1639,7 +1605,7 @@ Return value:
 ;; at least two new user variables, and there is no w3-fetch-noselect.
 ;; So instead, we just fake it with a slow save-window-excursion.
 
-(defun ffap-other-window nil
+(defun ffap-other-window ()
   "Like `ffap', but put buffer in another window.
 Only intended for interactive use."
   (interactive)
@@ -1652,7 +1618,7 @@ Only intended for interactive use."
        (current-buffer)))
     value))
 
-(defun ffap-other-frame nil
+(defun ffap-other-frame ()
   "Like `ffap', but put buffer in another frame.
 Only intended for interactive use."
   (interactive)
@@ -1672,6 +1638,13 @@ Only intended for interactive use."
       (set-window-dedicated-p win wdp))
     value))
 
+(defun ffap--toggle-read-only (buffer-or-list)
+  (dolist (buffer (if (listp buffer-or-list)
+                     buffer-or-list
+                   (list buffer-or-list)))
+    (with-current-buffer buffer
+      (read-only-mode 1))))
+
 (defun ffap-read-only ()
   "Like `ffap', but mark buffer as read-only.
 Only intended for interactive use."
@@ -1679,8 +1652,7 @@ Only intended for interactive use."
   (let ((value (call-interactively 'ffap)))
     (unless (or (bufferp value) (bufferp (car-safe value)))
       (setq value (current-buffer)))
-    (mapc (lambda (b) (with-current-buffer b (toggle-read-only 1)))
-         (if (listp value) value (list value)))
+    (ffap--toggle-read-only value)
     value))
 
 (defun ffap-read-only-other-window ()
@@ -1688,8 +1660,7 @@ Only intended for interactive use."
 Only intended for interactive use."
   (interactive)
   (let ((value (ffap-other-window)))
-    (mapc (lambda (b) (with-current-buffer b (toggle-read-only 1)))
-         (if (listp value) value (list value)))
+    (ffap--toggle-read-only value)
     value))
 
 (defun ffap-read-only-other-frame ()
@@ -1697,8 +1668,7 @@ Only intended for interactive use."
 Only intended for interactive use."
   (interactive)
   (let ((value (ffap-other-frame)))
-    (mapc (lambda (b) (with-current-buffer b (toggle-read-only 1)))
-         (if (listp value) value (list value)))
+    (ffap--toggle-read-only value)
     value))
 
 (defun ffap-alternate-file ()
@@ -1708,6 +1678,22 @@ Only intended for interactive use."
   (let ((ffap-file-finder 'find-alternate-file))
     (call-interactively 'ffap)))
 
+(defun ffap-alternate-file-other-window ()
+  "Like `ffap' and `find-alternate-file-other-window'.
+Only intended for interactive use."
+  (interactive)
+  (let ((ffap-file-finder 'find-alternate-file-other-window))
+    (call-interactively 'ffap)))
+
+(defun ffap-literally ()
+  "Like `ffap' and command `find-file-literally'.
+Only intended for interactive use."
+  (interactive)
+  (let ((ffap-file-finder 'find-file-literally))
+    (call-interactively 'ffap)))
+
+(defalias 'find-file-literally-at-point 'ffap-literally)
+
 \f
 ;;; Bug Reporter:
 
@@ -1720,15 +1706,15 @@ Only intended for interactive use."
 ;; If you do not like these bindings, write versions with whatever
 ;; bindings you would prefer.
 
-(defun ffap-ro-mode-hook nil
+(defun ffap-ro-mode-hook ()
   "Bind `ffap-next' and `ffap-menu' to M-l and M-m, resp."
   (local-set-key "\M-l" 'ffap-next)
-  (local-set-key "\M-m" 'ffap-menu)
-  )
+  (local-set-key "\M-m" 'ffap-menu))
 
-(defun ffap-gnus-hook nil
+(defun ffap-gnus-hook ()
   "Bind `ffap-gnus-next' and `ffap-gnus-menu' to M-l and M-m, resp."
-  (set (make-local-variable 'ffap-foo-at-bar-prefix) "news") ; message-id's
+  ;; message-id's
+  (setq-local thing-at-point-default-mail-uri-scheme "news")
   ;; Note "l", "L", "m", "M" are taken:
   (local-set-key "\M-l" 'ffap-gnus-next)
   (local-set-key "\M-m" 'ffap-gnus-menu))
@@ -1759,53 +1745,48 @@ Only intended for interactive use."
        (eval form)
       (pop-to-buffer sb))))
 
-(defun ffap-gnus-next nil
+(defun ffap-gnus-next ()
   "Run `ffap-next' in the gnus article buffer."
   (interactive) (ffap-gnus-wrapper '(ffap-next nil t)))
 
-(defun ffap-gnus-menu nil
+(defun ffap-gnus-menu ()
   "Run `ffap-menu' in the gnus article buffer."
   (interactive) (ffap-gnus-wrapper '(ffap-menu)))
 
 \f
-(defcustom dired-at-point-require-prefix nil
-  "If set, reverses the prefix argument to `dired-at-point'.
-This is nil so neophytes notice ffap.  Experts may prefer to disable
-ffap most of the time."
-  :type 'boolean
-  :group 'ffap
-  :version "20.3")
 
 ;;;###autoload
 (defun dired-at-point (&optional filename)
-  "Start Dired, defaulting to file at point.  See `ffap'."
+  "Start Dired, defaulting to file at point.  See `ffap'.
+If `dired-at-point-require-prefix' is set, the prefix meaning is reversed."
   (interactive)
-  (if (and (interactive-p)
+  (if (and (called-interactively-p 'interactive)
           (if dired-at-point-require-prefix
               (not current-prefix-arg)
             current-prefix-arg))
       (let (current-prefix-arg)                ; already interpreted
        (call-interactively ffap-directory-finder))
     (or filename (setq filename (dired-at-point-prompter)))
-    (cond
-     ((ffap-url-p filename)
-      (funcall ffap-url-fetcher filename))
-     ((and ffap-dired-wildcards
-          (string-match ffap-dired-wildcards filename))
-      (funcall ffap-directory-finder filename))
-     ((file-exists-p filename)
-      (if (file-directory-p filename)
+    (let ((url (ffap-url-p filename)))
+      (cond
+       (url
+       (funcall ffap-url-fetcher url))
+       ((and ffap-dired-wildcards
+            (string-match ffap-dired-wildcards filename))
+       (funcall ffap-directory-finder filename))
+       ((file-exists-p filename)
+       (if (file-directory-p filename)
+           (funcall ffap-directory-finder
+                    (expand-file-name filename))
          (funcall ffap-directory-finder
-                  (expand-file-name filename))
-       (funcall ffap-directory-finder
-                (concat (expand-file-name filename) "*"))))
-     ((and (file-writable-p
-            (or (file-name-directory (directory-file-name filename))
-                filename))
-           (y-or-n-p "Directory does not exist, create it? "))
-      (make-directory filename)
-      (funcall ffap-directory-finder filename))
-     ((error "No such file or directory `%s'" filename)))))
+                  (concat (expand-file-name filename) "*"))))
+       ((and (file-writable-p
+             (or (file-name-directory (directory-file-name filename))
+                 filename))
+            (y-or-n-p "Directory does not exist, create it? "))
+       (make-directory filename)
+       (funcall ffap-directory-finder filename))
+       ((error "No such file or directory `%s'" filename))))))
 
 (defun dired-at-point-prompter (&optional guess)
   ;; Does guess and prompt step for find-file-at-point.
@@ -1818,23 +1799,23 @@ ffap most of the time."
        (ffap-url-regexp "Dired file or URL: ")
        (t "Dired file: "))
        (prog1
-          (setq guess (or guess
-                           (let ((guess (ffap-guesser)))
-                             (if (or (not guess)
-                                     (ffap-url-p guess)
-                                     (ffap-file-remote-p guess))
-                                 guess
-                               (setq guess (abbreviate-file-name
-                                            (expand-file-name guess)))
-                               (cond
-                                ;; Interpret local directory as a directory.
-                                ((file-directory-p guess)
-                                 (file-name-as-directory guess))
-                                ;; Get directory component from local files.
-                                ((file-regular-p guess)
-                                 (file-name-directory guess))
-                                (guess))))
-                           ))
+          (setq guess
+                (let ((guess (or guess (ffap-guesser))))
+                  (cond
+                   ((null guess) nil)
+                   ((ffap-url-p guess))
+                   ((ffap-file-remote-p guess)
+                    guess)
+                   ((progn
+                      (setq guess (abbreviate-file-name
+                                   (expand-file-name guess)))
+                      ;; Interpret local directory as a directory.
+                      (file-directory-p guess))
+                    (file-name-as-directory guess))
+                   ;; Get directory component from local files.
+                   ((file-regular-p guess)
+                    (file-name-directory guess))
+                   (guess))))
         (and guess (ffap-highlight))))
     (ffap-highlight t)))
 \f
@@ -1877,11 +1858,28 @@ Only intended for interactive use."
     (call-interactively 'dired-at-point)))
 
 \f
+;;; Hooks to put in `file-name-at-point-functions':
+
+;;;###autoload
+(defun ffap-guess-file-name-at-point ()
+  "Try to get a file name at point.
+This hook is intended to be put in `file-name-at-point-functions'."
+  (let ((guess (ffap-guesser)))
+    (when (stringp guess)
+      (let ((url (ffap-url-p guess)))
+       (or url
+           (progn
+             (unless (ffap-file-remote-p guess)
+               (setq guess
+                     (abbreviate-file-name (expand-file-name guess))))
+             (if (file-directory-p guess)
+                 (file-name-as-directory guess)
+               guess)))))))
+\f
 ;;; Offer default global bindings (`ffap-bindings'):
 
 (defvar ffap-bindings
-   '(
-     (global-set-key [S-mouse-3] 'ffap-at-mouse)
+   '((global-set-key [S-mouse-3] 'ffap-at-mouse)
      (global-set-key [C-S-mouse-3] 'ffap-menu)
 
      (global-set-key "\C-x\C-f" 'find-file-at-point)
@@ -1901,16 +1899,14 @@ Only intended for interactive use."
      (add-hook 'gnus-summary-mode-hook 'ffap-gnus-hook)
      (add-hook 'gnus-article-mode-hook 'ffap-gnus-hook)
      (add-hook 'vm-mode-hook 'ffap-ro-mode-hook)
-     (add-hook 'rmail-mode-hook 'ffap-ro-mode-hook)
-     ;; (setq dired-x-hands-off-my-keys t) ; the default
-     )
+     (add-hook 'rmail-mode-hook 'ffap-ro-mode-hook))
      "List of binding forms evaluated by function `ffap-bindings'.
 A reasonable ffap installation needs just this one line:
   (ffap-bindings)
 Of course if you do not like these bindings, just roll your own!")
 
 ;;;###autoload
-(defun ffap-bindings nil
+(defun ffap-bindings ()
   "Evaluate the forms in variable `ffap-bindings'."
   (interactive)
   (eval (cons 'progn ffap-bindings)))
@@ -1918,5 +1914,4 @@ Of course if you do not like these bindings, just roll your own!")
 \f
 (provide 'ffap)
 
-;; arch-tag: 9dd3e88a-5dec-4607-bd57-60ae9ede8ebc
 ;;; ffap.el ends here