ffap.el 74.5 KB
Newer Older
1 2
;;; ffap.el --- find file (or url) at point

Paul Eggert's avatar
Paul Eggert committed
3
;; Copyright (C) 1995-1997, 2000-2017 Free Software Foundation, Inc.
4

Erik Naggum's avatar
Erik Naggum committed
5
;; Author: Michelangelo Grigni <mic@mathcs.emory.edu>
6
;; Maintainer: emacs-devel@gnu.org
7
;; Created: 29 Mar 1993
Dan Nicolaescu's avatar
Dan Nicolaescu committed
8
;; Keywords: files, hypermedia, matching, mouse, convenience
9
;; X-URL: ftp://ftp.mathcs.emory.edu/pub/mic/emacs/
Richard M. Stallman's avatar
Richard M. Stallman committed
10 11 12

;; This file is part of GNU Emacs.

13
;; GNU Emacs is free software: you can redistribute it and/or modify
Richard M. Stallman's avatar
Richard M. Stallman committed
14
;; it under the terms of the GNU General Public License as published by
15 16
;; the Free Software Foundation, either version 3 of the License, or
;; (at your option) any later version.
Richard M. Stallman's avatar
Richard M. Stallman committed
17 18 19 20 21 22 23

;; GNU Emacs is distributed in the hope that it will be useful,
;; but WITHOUT ANY WARRANTY; without even the implied warranty of
;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
;; GNU General Public License for more details.

;; You should have received a copy of the GNU General Public License
24
;; along with GNU Emacs.  If not, see <http://www.gnu.org/licenses/>.
Richard M. Stallman's avatar
Richard M. Stallman committed
25 26


27
;;; Commentary:
Richard M. Stallman's avatar
Richard M. Stallman committed
28
;;
29 30
;; Command find-file-at-point replaces find-file.  With a prefix, it
;; behaves exactly like find-file.  Without a prefix, it first tries
31
;; to guess a default file or URL from the text around the point
32 33 34
;; (`ffap-require-prefix' swaps these behaviors).  This is useful for
;; following references in situations such as mail or news buffers,
;; README's, MANIFEST's, and so on.  Submit bugs or suggestions with
35
;; M-x report-emacs-bug.
Richard M. Stallman's avatar
Richard M. Stallman committed
36
;;
37
;; For the default installation, add this line to your init file:
Richard M. Stallman's avatar
Richard M. Stallman committed
38
;;
39
;; (ffap-bindings)                      ; do default key bindings
Richard M. Stallman's avatar
Richard M. Stallman committed
40
;;
41
;; ffap-bindings makes the following global key bindings:
Richard M. Stallman's avatar
Richard M. Stallman committed
42
;;
43 44 45 46 47 48 49 50 51 52 53 54 55 56 57
;; C-x C-f		find-file-at-point (abbreviated as ffap)
;; C-x C-r		ffap-read-only
;; C-x C-v		ffap-alternate-file
;;
;; C-x d		dired-at-point
;; C-x C-d		ffap-list-directory
;;
;; C-x 4 f		ffap-other-window
;; C-x 4 r		ffap-read-only-other-window
;; C-x 4 d		ffap-dired-other-window
;;
;; C-x 5 f		ffap-other-frame
;; C-x 5 r		ffap-read-only-other-frame
;; C-x 5 d		ffap-dired-other-frame
;;
58
;; S-mouse-3     ffap-at-mouse
59
;; C-S-mouse-3   ffap-menu
Richard M. Stallman's avatar
Richard M. Stallman committed
60
;;
61 62
;; ffap-bindings also adds hooks to make the following local bindings
;; in vm, gnus, and rmail:
Richard M. Stallman's avatar
Richard M. Stallman committed
63
;;
64 65
;; M-l         ffap-next, or ffap-gnus-next in gnus (l == "link")
;; M-m         ffap-menu, or ffap-gnus-menu in gnus (m == "menu")
Richard M. Stallman's avatar
Richard M. Stallman committed
66
;;
67 68
;; If you do not like these bindings, modify the variable
;; `ffap-bindings', or write your own.
Richard M. Stallman's avatar
Richard M. Stallman committed
69
;;
70 71 72 73
;; If you use ange-ftp, browse-url, complete, efs, or w3, it is best
;; to load or autoload them before ffap.  If you use ff-paths, load it
;; afterwards.  Try apropos {C-h a ffap RET} to get a list of the many
;; option variables.  In particular, if ffap is slow, try these:
Richard M. Stallman's avatar
Richard M. Stallman committed
74
;;
75 76
;; (setq ffap-alist nil)                ; faster, dumber prompting
;; (setq ffap-machine-p-known 'accept)  ; no pinging
77
;; (setq ffap-url-regexp nil)           ; disable URL features in ffap
78
;; (setq ffap-shell-prompt-regexp nil)  ; disable shell prompt stripping
Richard M. Stallman's avatar
Richard M. Stallman committed
79
;;
80 81
;; ffap uses `browse-url' (if found, else `w3-fetch') to fetch URL's.
;; For a hairier `ffap-url-fetcher', try ffap-url.el (same ftp site).
82
;; Also, you can add `ffap-menu-rescan' to various hooks to fontify
83
;; the file and URL references within a buffer.
84

85 86 87 88 89 90 91

;;; Change Log:
;;
;; The History and Contributors moved to ffap.LOG (same ftp site),
;; which also has some old examples and commentary from ffap 1.5.


92
;;; Todo list:
Richard M. Stallman's avatar
Richard M. Stallman committed
93
;; * let "/dir/file#key" jump to key (tag or regexp) in /dir/file
94
;; * find file of symbol if TAGS is loaded (like above)
95 96
;; * break long menus into multiple panes (like imenu?)
;; * notice node in "(dired)Virtual Dired" (quotes, parentheses, whitespace)
Richard M. Stallman's avatar
Richard M. Stallman committed
97
;; * notice "machine.dom blah blah blah dir/file" (how?)
98
;; * as w3 becomes standard, rewrite to rely more on its functions
99 100
;; * regexp options for ffap-string-at-point, like font-lock (MCOOK)
;; * v19: could replace `ffap-locate-file' with a quieter `locate-library'
101 102
;; * handle "$(VAR)" in Makefiles
;; * use the font-lock machinery
Richard M. Stallman's avatar
Richard M. Stallman committed
103 104 105 106


;;; Code:

107
(require 'url-parse)
108
(require 'thingatpt)
109

110
(define-obsolete-variable-alias 'ffap-version 'emacs-version "23.2")
Richard M. Stallman's avatar
Richard M. Stallman committed
111

112 113
(defgroup ffap nil
  "Find file or URL at point."
114 115
  ;; Dead 2009/07/05.
;;  :link '(url-link :tag "URL" "ftp://ftp.mathcs.emory.edu/pub/mic/emacs/")
Dan Nicolaescu's avatar
Dan Nicolaescu committed
116 117
  :group 'matching
  :group 'convenience)
118

119 120 121 122 123
;; The code is organized in pages, separated by formfeed characters.
;; See the next two pages for standard customization ideas.


;;; User Variables:
124

Stefan Monnier's avatar
Stefan Monnier committed
125 126 127
(defun ffap-symbol-value (sym &optional default)
  "Return value of symbol SYM, if bound, or DEFAULT otherwise."
  (if (boundp sym) (symbol-value sym) default))
Richard M. Stallman's avatar
Richard M. Stallman committed
128

129 130 131 132 133
(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.
  "[$%><]*"
Juanma Barranquero's avatar
Juanma Barranquero committed
134
  "Paths matching this regexp are stripped off the shell prompt.
135 136 137 138 139 140
If nil, ffap doesn't do shell prompt stripping."
  :type '(choice (const :tag "Disable" nil)
		  (const :tag "Standard" "[$%><]*")
		   regexp)
  :group 'ffap)

141
(defcustom ffap-ftp-regexp "\\`/[^/:]+:"
Lute Kamstra's avatar
Lute Kamstra committed
142
  "File names matching this regexp are treated as remote ffap.
Richard M. Stallman's avatar
Richard M. Stallman committed
143
If nil, ffap neither recognizes nor generates such names."
144
  :type '(choice (const :tag "Disable" nil)
145
		 (const :tag "Standard" "\\`/[^/:]+:")
146 147 148 149
		 regexp)
  :group 'ffap)

(defcustom ffap-url-unwrap-local t
150 151 152 153
  "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'."
154 155 156
  :type 'boolean
  :group 'ffap)

157 158 159 160 161 162
(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
163
  :version "24.3")
164

165 166 167 168 169 170
(defcustom ffap-lax-url t
  "If non-nil, allow lax URL matching.
The default non-nil value might produce false URLs in C++ code
with symbols like \"std::find\".  On the other hand, setting
this to nil will disable recognition of URLs that are not
well-formed, such as \"user@host\" or \"<user@host>\"."
171 172
  :type 'boolean
  :group 'ffap
173
  :version "25.2")                      ; nil -> t
174

175
(defcustom ffap-ftp-default-user "anonymous"
Juanma Barranquero's avatar
Juanma Barranquero committed
176
  "User name in FTP file names generated by `ffap-host-to-path'.
177
Note this name may be omitted if it equals the default
Juanma Barranquero's avatar
Juanma Barranquero committed
178
\(either `efs-default-user' or `ange-ftp-default-user')."
179
  :type 'string
180
  :group 'ffap)
Richard M. Stallman's avatar
Richard M. Stallman committed
181

182
(defcustom ffap-rfs-regexp
Richard M. Stallman's avatar
Richard M. Stallman committed
183 184 185
  ;; Remote file access built into file system?  HP rfa or Andrew afs:
  "\\`/\\(afs\\|net\\)/."
  ;; afs only: (and (file-exists-p "/afs") "\\`/afs/.")
Lute Kamstra's avatar
Lute Kamstra committed
186
  "Matching file names are treated as remote.  Use nil to disable."
187 188
  :type 'regexp
  :group 'ffap)
Richard M. Stallman's avatar
Richard M. Stallman committed
189 190 191

(defvar ffap-url-regexp
  (concat
192
   "\\("
Richard M. Stallman's avatar
Richard M. Stallman committed
193 194
   "news\\(post\\)?:\\|mailto:\\|file:" ; no host ok
   "\\|"
195
   "\\(ftp\\|https?\\|telnet\\|gopher\\|www\\|wais\\)://" ; needs host
196
   "\\)")
Juanma Barranquero's avatar
Juanma Barranquero committed
197
  "Regexp matching the beginning of a URI, for ffap.
198
If the value is nil, disable URL-matching features in ffap.")
Richard M. Stallman's avatar
Richard M. Stallman committed
199

200
(defcustom ffap-foo-at-bar-prefix "mailto"
Lute Kamstra's avatar
Lute Kamstra committed
201
  "Presumed URL prefix type of strings like \"<foo.9z@bar>\".
202
Sensible values are nil, \"news\", or \"mailto\"."
203 204 205 206 207
  :type '(choice (const "mailto")
		 (const "news")
		 (const :tag "Disable" nil)
		 ;; string -- possible, but not really useful
		 )
208
  :group 'ffap)
Richard M. Stallman's avatar
Richard M. Stallman committed
209 210


211
;;; Peanut Gallery (More User Variables):
212
;;
Richard M. Stallman's avatar
Richard M. Stallman committed
213 214 215
;; Users of ffap occasionally suggest new features.  If I consider
;; those features interesting but not clear winners (a matter of
;; personal taste) I try to leave options to enable them.  Read
216
;; through this section for features that you like, put an appropriate
217
;; enabler in your init file.
Richard M. Stallman's avatar
Richard M. Stallman committed
218

219
(defcustom ffap-dired-wildcards "[*?][^/]*\\'"
Lute Kamstra's avatar
Lute Kamstra committed
220
  "A regexp matching filename wildcard characters, or nil.
221

222
If `find-file-at-point' gets a filename matching this pattern,
223 224 225 226 227 228 229
and `ffap-pass-wildcards-to-dired' is nil, it passes it on to
`find-file' with non-nil WILDCARDS argument, which expands
wildcards and visits multiple files.  To visit a file whose name
contains wildcard characters you can suppress wildcard expansion
by setting `find-file-wildcards'.  If `find-file-at-point' gets a
filename matching this pattern and `ffap-pass-wildcards-to-dired'
is non-nil, it passes it on to `dired'.
230 231 232

If `dired-at-point' gets a filename matching this pattern,
it passes it on to `dired'."
233 234 235 236
  :type '(choice (const :tag "Disable" nil)
		 (const :tag "Enable" "[*?][^/]*\\'")
		 ;; regexp -- probably not useful
		 )
237
  :group 'ffap)
Richard M. Stallman's avatar
Richard M. Stallman committed
238

239
(defcustom ffap-pass-wildcards-to-dired nil
Juanma Barranquero's avatar
Juanma Barranquero committed
240
  "If non-nil, pass filenames matching `ffap-dired-wildcards' to Dired."
241 242 243
  :type 'boolean
  :group 'ffap)

244
(defcustom ffap-newfile-prompt nil
245 246
  ;; Suggestion from RHOGEE, 11 Jul 1994.  Disabled, I think this is
  ;; better handled by `find-file-not-found-hooks'.
Lute Kamstra's avatar
Lute Kamstra committed
247
  "Whether `find-file-at-point' prompts about a nonexistent file."
248 249
  :type 'boolean
  :group 'ffap)
Richard M. Stallman's avatar
Richard M. Stallman committed
250

251
(defcustom ffap-require-prefix nil
252
  ;; Suggestion from RHOGEE, 20 Oct 1994.
Lute Kamstra's avatar
Lute Kamstra committed
253
  "If set, reverses the prefix argument to `find-file-at-point'.
254
This is nil so neophytes notice ffap.  Experts may prefer to disable
255 256 257 258 259
ffap most of the time."
  :type 'boolean
  :group 'ffap)

(defcustom ffap-file-finder 'find-file
Lute Kamstra's avatar
Lute Kamstra committed
260
  "The command called by `find-file-at-point' to find a file."
261
  :type 'function
262 263
  :group 'ffap
  :risky t)
Richard M. Stallman's avatar
Richard M. Stallman committed
264

265
(defcustom ffap-directory-finder 'dired
Lute Kamstra's avatar
Lute Kamstra committed
266
  "The command called by `dired-at-point' to find a directory."
267
  :type 'function
268 269
  :group 'ffap
  :risky t)
270

271
(defcustom ffap-url-fetcher 'browse-url
Lute Kamstra's avatar
Lute Kamstra committed
272
  "A function of one argument, called by ffap to fetch an URL.
Karl Heuer's avatar
Karl Heuer committed
273
For a fancy alternative, get `ffap-url.el'."
274
  :type '(choice (const browse-url)
275
		 function)
276 277 278 279 280 281 282 283 284 285 286 287 288
  :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
289
  :group 'ffap)
290 291 292

(defcustom dired-at-point-require-prefix nil
  "If non-nil, reverse the prefix argument to `dired-at-point'.
Juanma Barranquero's avatar
Juanma Barranquero committed
293 294
This is nil so neophytes notice ffap.  Experts may prefer to
disable ffap most of the time."
295 296 297
  :type 'boolean
  :group 'ffap
  :version "20.3")
Richard M. Stallman's avatar
Richard M. Stallman committed
298 299


300 301
;;; Compatibility:
;;
Richard M. Stallman's avatar
Richard M. Stallman committed
302 303 304
;; This version of ffap supports only the Emacs it is distributed in.
;; See the ftp site for a more general version.  The following
;; functions are necessary "leftovers" from the more general version.
305

Stefan Monnier's avatar
Stefan Monnier committed
306
(defun ffap-mouse-event ()		; current mouse event, or nil
307 308 309
  (and (listp last-nonmenu-event) last-nonmenu-event))
(defun ffap-event-buffer (event)
  (window-buffer (car (event-start event))))
310 311 312


;;; Find Next Thing in buffer (`ffap-next'):
Richard M. Stallman's avatar
Richard M. Stallman committed
313
;;
314 315 316
;; Original ffap-next-url (URL's only) from RPECK 30 Mar 1995.  Since
;; then, broke it up into ffap-next-guess (noninteractive) and
;; ffap-next (a command).  It now work on files as well as url's.
Richard M. Stallman's avatar
Richard M. Stallman committed
317

318 319 320 321
(defvar ffap-next-guess nil
  "Last value returned by `ffap-next-guess'.")

(defvar ffap-string-at-point-region '(1 1)
322
  "List (BEG END), last region returned by the function `ffap-string-at-point'.")
323

Richard M. Stallman's avatar
Richard M. Stallman committed
324
(defun ffap-next-guess (&optional back lim)
325
  "Move point to next file or URL, and return it as a string.
326
If nothing is found, leave point at limit and return nil.
Richard M. Stallman's avatar
Richard M. Stallman committed
327 328 329 330 331 332 333 334 335 336 337 338 339 340 341
Optional BACK argument makes search backwards.
Optional LIM argument limits the search.
Only considers strings that match `ffap-next-regexp'."
  (or lim (setq lim (if back (point-min) (point-max))))
  (let (guess)
    (while (not (or guess (eq (point) lim)))
      (funcall (if back 're-search-backward 're-search-forward)
	       ffap-next-regexp lim 'move)
      (setq guess (ffap-guesser)))
    ;; Go to end, so we do not get same guess twice:
    (goto-char (nth (if back 0 1) ffap-string-at-point-region))
    (setq ffap-next-guess guess)))

;;;###autoload
(defun ffap-next (&optional back wrap)
342
  "Search buffer for next file or URL, and run ffap.
Richard M. Stallman's avatar
Richard M. Stallman committed
343 344
Optional argument BACK says to search backwards.
Optional argument WRAP says to try wrapping around if necessary.
Juanma Barranquero's avatar
Juanma Barranquero committed
345
Interactively: use a single prefix \\[universal-argument] to search backwards,
Richard M. Stallman's avatar
Richard M. Stallman committed
346
double prefix to wrap forward, triple to wrap backwards.
347
Actual search is done by the function `ffap-next-guess'."
Richard M. Stallman's avatar
Richard M. Stallman committed
348 349 350 351 352 353 354 355 356 357 358 359 360 361
  (interactive
   (cdr (assq (prefix-numeric-value current-prefix-arg)
	      '((1) (4 t) (16 nil t) (64 t t)))))
  (let ((pt (point))
	(guess (ffap-next-guess back)))
    ;; Try wraparound if necessary:
    (and (not guess) wrap
	 (goto-char (if back (point-max) (point-min)))
	 (setq guess (ffap-next-guess back pt)))
    (if guess
	(progn
	  (sit-for 0)			; display point movement
	  (find-file-at-point (ffap-prompter guess)))
      (goto-char pt)			; restore point
362
      (message "No %sfiles or URL's found"
Richard M. Stallman's avatar
Richard M. Stallman committed
363 364 365
	       (if wrap "" "more ")))))

(defun ffap-next-url (&optional back wrap)
366
  "Like `ffap-next', but search with `ffap-url-regexp'."
Richard M. Stallman's avatar
Richard M. Stallman committed
367 368
  (interactive)
  (let ((ffap-next-regexp ffap-url-regexp))
369
    (if (called-interactively-p 'interactive)
Richard M. Stallman's avatar
Richard M. Stallman committed
370 371 372 373
	(call-interactively 'ffap-next)
      (ffap-next back wrap))))


374
;;; Machines (`ffap-machine-p'):
Richard M. Stallman's avatar
Richard M. Stallman committed
375 376 377 378

;; I cannot decide a "best" strategy here, so these are variables.  In
;; particular, if `Pinging...' is broken or takes too long on your
;; machine, try setting these all to accept or reject.
379
(defcustom ffap-machine-p-local 'reject	; this happens often
Lute Kamstra's avatar
Lute Kamstra committed
380
  "What `ffap-machine-p' does with hostnames that have no domain.
381
Value should be a symbol, one of `ping', `accept', and `reject'."
382 383 384 385
  :type '(choice (const ping)
		 (const accept)
		 (const reject))
  :group 'ffap)
Karl Heuer's avatar
Karl Heuer committed
386
(defcustom ffap-machine-p-known 'ping	; `accept' for higher speed
Lute Kamstra's avatar
Lute Kamstra committed
387
  "What `ffap-machine-p' does with hostnames that have a known domain.
Karl Heuer's avatar
Karl Heuer committed
388 389
Value should be a symbol, one of `ping', `accept', and `reject'.
See `mail-extr.el' for the known domains."
390 391 392 393 394
  :type '(choice (const ping)
		 (const accept)
		 (const reject))
  :group 'ffap)
(defcustom ffap-machine-p-unknown 'reject
Lute Kamstra's avatar
Lute Kamstra committed
395
  "What `ffap-machine-p' does with hostnames that have an unknown domain.
Karl Heuer's avatar
Karl Heuer committed
396 397
Value should be a symbol, one of `ping', `accept', and `reject'.
See `mail-extr.el' for the known domains."
398 399 400 401
  :type '(choice (const ping)
		 (const accept)
		 (const reject))
  :group 'ffap)
402 403 404 405

(defun ffap-what-domain (domain)
  ;; Like what-domain in mail-extr.el, returns string or nil.
  (require 'mail-extr)
Stefan Monnier's avatar
Stefan Monnier committed
406 407
  (let ((ob (or (ffap-symbol-value 'mail-extr-all-top-level-domains)
		(ffap-symbol-value 'all-top-level-domains)))) ; XEmacs
408
    (and ob (get (intern-soft (downcase domain) ob) 'domain-name))))
409 410 411 412 413 414

(defun ffap-machine-p (host &optional service quiet strategy)
  "Decide whether HOST is the name of a real, reachable machine.
Depending on the domain (none, known, or unknown), follow the strategy
named by the variable `ffap-machine-p-local', `ffap-machine-p-known',
or `ffap-machine-p-unknown'.  Pinging uses `open-network-stream'.
Juanma Barranquero's avatar
Juanma Barranquero committed
415
Optional SERVICE specifies the port used (default \"discard\").
Richard M. Stallman's avatar
Richard M. Stallman committed
416
Optional QUIET flag suppresses the \"Pinging...\" message.
417
Optional STRATEGY overrides the three variables above.
Richard M. Stallman's avatar
Richard M. Stallman committed
418
Returned values:
419 420 421
 t       means that HOST answered.
`accept' means the relevant variable told us to accept.
\"mesg\"   means HOST exists, but does not respond for some reason."
422 423 424 425 426 427 428
  ;; Try some (Emory local):
  ;; (ffap-machine-p "ftp" nil nil 'ping)
  ;; (ffap-machine-p "nonesuch" nil nil 'ping)
  ;; (ffap-machine-p "ftp.mathcs.emory.edu" nil nil 'ping)
  ;; (ffap-machine-p "mathcs" 5678 nil 'ping)
  ;; (ffap-machine-p "foo.bonk" nil nil 'ping)
  ;; (ffap-machine-p "foo.bonk.com" nil nil 'ping)
Juanma Barranquero's avatar
Juanma Barranquero committed
429
  (if (or (string-match "[^-[:alnum:].]" host) ; Invalid chars (?)
430
	  (not (string-match "[^0-9]" host))) ; 1: a number? 2: quick reject
Richard M. Stallman's avatar
Richard M. Stallman committed
431 432 433 434
      nil
    (let* ((domain
	    (and (string-match "\\.[^.]*$" host)
		 (downcase (substring host (1+ (match-beginning 0))))))
435 436 437 438 439 440
	   (what-domain (if domain (ffap-what-domain domain) "Local")))
      (or strategy
	  (setq strategy
		(cond ((not domain) ffap-machine-p-local)
		      ((not what-domain) ffap-machine-p-unknown)
		      (t ffap-machine-p-known))))
Richard M. Stallman's avatar
Richard M. Stallman committed
441 442 443
      (cond
       ((eq strategy 'accept) 'accept)
       ((eq strategy 'reject) nil)
444
       ((not (fboundp 'open-network-stream)) nil)
Richard M. Stallman's avatar
Richard M. Stallman committed
445 446 447
       ;; assume (eq strategy 'ping)
       (t
	(or quiet
448 449
	    (if (stringp what-domain)
		(message "Pinging %s (%s)..." host what-domain)
Richard M. Stallman's avatar
Richard M. Stallman committed
450 451 452 453 454 455 456 457 458 459 460
	      (message "Pinging %s ..." host)))
	(condition-case error
	    (progn
	      (delete-process
	       (open-network-stream
		"ffap-machine-p" nil host (or service "discard")))
	      t)
	  (error
	   (let ((mesg (car (cdr error))))
	     (cond
	      ;; v18:
461 462
	      ((string-match "\\(^Unknown host\\|Name or service not known$\\)"
			     mesg) nil)
Richard M. Stallman's avatar
Richard M. Stallman committed
463 464 465 466 467 468 469 470 471
	      ((string-match "not responding$" mesg) mesg)
	      ;; v19:
	      ;; (file-error "connection failed" "permission denied"
	      ;;             "nonesuch" "ffap-machine-p")
	      ;; (file-error "connection failed" "host is unreachable"
	      ;;	     "gopher.house.gov" "ffap-machine-p")
	      ;; (file-error "connection failed" "address already in use"
	      ;;	     "ftp.uu.net" "ffap-machine-p")
	      ((equal mesg "connection failed")
472
	       (if (string= (downcase (nth 2 error)) "permission denied")
Richard M. Stallman's avatar
Richard M. Stallman committed
473
		   nil			; host does not exist
474
		 ;; Other errors mean the host exists:
Richard M. Stallman's avatar
Richard M. Stallman committed
475 476 477 478
		 (nth 2 error)))
	      ;; Could be "Unknown service":
	      (t (signal (car error) (cdr error))))))))))))

479 480 481

;;; Possibly Remote Resources:

Richard M. Stallman's avatar
Richard M. Stallman committed
482
(defun ffap-replace-file-component (fullname name)
483
  "In remote FULLNAME, replace path with NAME.  May return nil."
484 485
  ;; Use efs if loaded, but do not load it otherwise.
  (if (fboundp 'efs-replace-path-component)
486
      (funcall 'efs-replace-path-component fullname name)
487 488 489
    (and (stringp fullname)
	 (stringp name)
	 (concat (file-remote-p fullname) name))))
Richard M. Stallman's avatar
Richard M. Stallman committed
490
;; (ffap-replace-file-component "/who@foo.com:/whatever" "/new")
491

492
(defun ffap-file-suffix (file)
Karl Heuer's avatar
Karl Heuer committed
493
  "Return trailing `.foo' suffix of FILE, or nil if none."
494 495 496 497 498 499 500 501 502 503 504
  (let ((pos (string-match "\\.[^./]*\\'" file)))
    (and pos (substring file pos nil))))

(defvar ffap-compression-suffixes '(".gz" ".Z")	; .z is mostly dead
  "List of suffixes tried by `ffap-file-exists-string'.")

(defun ffap-file-exists-string (file &optional nomodify)
  ;; Early jka-compr versions modified file-exists-p to return the
  ;; filename, maybe modified by adding a suffix like ".gz".  That
  ;; broke the interface of file-exists-p, so it was later dropped.
  ;; Here we document and simulate the old behavior.
Karl Heuer's avatar
Karl Heuer committed
505
  "Return FILE (maybe modified) if the file exists, else nil.
506 507 508 509 510 511 512 513 514 515 516 517 518 519 520 521 522
When using jka-compr (a.k.a. `auto-compression-mode'), the returned
name may have a suffix added from `ffap-compression-suffixes'.
The optional NOMODIFY argument suppresses the extra search."
  (cond
   ((not file) nil)			; quietly reject nil
   ((file-exists-p file) file)		; try unmodified first
   ;; three reasons to suppress search:
   (nomodify nil)
   ((not (rassq 'jka-compr-handler file-name-handler-alist)) nil)
   ((member (ffap-file-suffix file) ffap-compression-suffixes) nil)
   (t					; ok, do the search
    (let ((list ffap-compression-suffixes) try ret)
      (while list
	(if (file-exists-p (setq try (concat file (car list))))
	    (setq ret try list nil)
	  (setq list (cdr list))))
      ret))))
523

Richard M. Stallman's avatar
Richard M. Stallman committed
524
(defun ffap-file-remote-p (filename)
Karl Heuer's avatar
Karl Heuer committed
525
  "If FILENAME looks remote, return it (maybe slightly improved)."
Richard M. Stallman's avatar
Richard M. Stallman committed
526
  ;; (ffap-file-remote-p "/user@foo.bar.com:/pub")
Richard M. Stallman's avatar
Richard M. Stallman committed
527
  ;; (ffap-file-remote-p "/cssun.mathcs.emory.edu://dir")
528
  ;; (ffap-file-remote-p "/ffap.el:80")
Richard M. Stallman's avatar
Richard M. Stallman committed
529 530
  (or (and ffap-ftp-regexp
	   (string-match ffap-ftp-regexp filename)
Paul Eggert's avatar
Paul Eggert committed
531
	   ;; Convert "/host.com://dir" to "/host:/dir", to handle a dying
Richard M. Stallman's avatar
Richard M. Stallman committed
532
	   ;; practice of advertising ftp files as "host.dom://filename".
Richard M. Stallman's avatar
Richard M. Stallman committed
533
	   (if (string-match "//" filename)
534 535 536
	       ;; (replace-match "/" nil nil filename)
	       (concat (substring filename 0 (1+ (match-beginning 0)))
		       (substring filename (match-end 0)))
Richard M. Stallman's avatar
Richard M. Stallman committed
537 538 539 540 541
	     filename))
      (and ffap-rfs-regexp
	   (string-match ffap-rfs-regexp filename)
	   filename)))

Stefan Monnier's avatar
Stefan Monnier committed
542
(defun ffap-machine-at-point ()
543 544
  "Return machine name at point if it exists, or nil."
  (let ((mach (ffap-string-at-point 'machine)))
Richard M. Stallman's avatar
Richard M. Stallman committed
545 546
    (and (ffap-machine-p mach) mach)))

Richard M. Stallman's avatar
Richard M. Stallman committed
547
(defsubst ffap-host-to-filename (host)
548
  "Convert HOST to something like \"/USER@HOST:\" or \"/HOST:\".
549
Looks at `ffap-ftp-default-user', returns \"\" for \"localhost\"."
550 551 552 553
  (if (equal host "localhost")
      ""
    (let ((user ffap-ftp-default-user))
      ;; Avoid including the user if it is same as default:
Stefan Monnier's avatar
Stefan Monnier committed
554 555
      (if (or (equal user (ffap-symbol-value 'ange-ftp-default-user))
	      (equal user (ffap-symbol-value 'efs-default-user)))
556 557
	  (setq user nil))
      (concat "/" user (and user "@") host ":"))))
558

Richard M. Stallman's avatar
Richard M. Stallman committed
559
(defun ffap-fixup-machine (mach)
Richard M. Stallman's avatar
Richard M. Stallman committed
560
  ;; Convert a hostname into an url, an ftp file name, or nil.
Richard M. Stallman's avatar
Richard M. Stallman committed
561 562
  (cond
   ((not (and ffap-url-regexp (stringp mach))) nil)
563
   ;; gopher.well.com
Richard M. Stallman's avatar
Richard M. Stallman committed
564 565
   ((string-match "\\`gopher[-.]" mach)	; or "info"?
    (concat "gopher://" mach "/"))
566
   ;; www.ncsa.uiuc.edu
Richard M. Stallman's avatar
Richard M. Stallman committed
567 568 569
   ((and (string-match "\\`w\\(ww\\|eb\\)[-.]" mach))
    (concat "http://" mach "/"))
   ;; More cases?  Maybe "telnet:" for archie?
Richard M. Stallman's avatar
Richard M. Stallman committed
570
   (ffap-ftp-regexp (ffap-host-to-filename mach))
Richard M. Stallman's avatar
Richard M. Stallman committed
571 572
   ))

573 574 575
(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)
Richard M. Stallman's avatar
Richard M. Stallman committed
576

577
(defsubst ffap-url-p (string)
Juanma Barranquero's avatar
Juanma Barranquero committed
578
  "If STRING looks like an URL, return it (maybe improved), else nil."
579 580 581 582 583
  (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))))))
Richard M. Stallman's avatar
Richard M. Stallman committed
584

585
;; Broke these out of ffap-fixup-url, for use of ffap-url package.
586 587 588 589 590 591 592 593 594 595 596 597 598 599 600 601 602 603 604 605 606 607 608 609 610 611 612 613 614 615 616 617
(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))))
Richard M. Stallman's avatar
Richard M. Stallman committed
618 619

(defun ffap-fixup-url (url)
620
  "Clean up URL and return it, maybe as a file name."
Richard M. Stallman's avatar
Richard M. Stallman committed
621 622
  (cond
   ((not (stringp url)) nil)
623 624
   ((and ffap-url-unwrap-local  (ffap-url-unwrap-local url)))
   ((and ffap-url-unwrap-remote (ffap-url-unwrap-remote url)))
625
   (url)))
Richard M. Stallman's avatar
Richard M. Stallman committed
626 627


Richard M. Stallman's avatar
Richard M. Stallman committed
628
;;; File Name Handling:
Richard M. Stallman's avatar
Richard M. Stallman committed
629
;;
630
;; The upcoming ffap-alist actions need various utilities to prepare
Richard M. Stallman's avatar
Richard M. Stallman committed
631
;; and search directories.  Too many features here.
632 633 634 635 636 637 638 639 640

;; (defun ffap-last (l) (while (cdr l) (setq l (cdr l))) l)
;; (defun ffap-splice (func inlist)
;;  "Equivalent to (apply 'nconc (mapcar FUNC INLIST)), but less consing."
;;  (let* ((head (cons 17 nil)) (last head))
;;    (while inlist
;;      (setcdr last (funcall func (car inlist)))
;;      (setq last (ffap-last last) inlist (cdr inlist)))
;;    (cdr head)))
Richard M. Stallman's avatar
Richard M. Stallman committed
641 642

(defun ffap-list-env (env &optional empty)
643
  "Return a list of strings parsed from environment variable ENV.
Juanma Barranquero's avatar
Juanma Barranquero committed
644
Optional EMPTY is the default list if (getenv ENV) is undefined, and
645 646 647 648 649 650
also is substituted for the first empty-string component, if there is one.
Uses `path-separator' to separate the path into substrings."
  ;; We cannot use parse-colon-path (files.el), since it kills
  ;; "//" entries using file-name-as-directory.
  ;; Similar: dired-split, TeX-split-string, and RHOGEE's psg-list-env
  ;; in ff-paths and bib-cite.  The EMPTY arg may help mimic kpathsea.
Richard M. Stallman's avatar
Richard M. Stallman committed
651 652
  (if (or empty (getenv env))		; should return something
      (let ((start 0) match dir ret)
653
	(setq env (concat (getenv env) path-separator))
654
	(while (setq match (string-match path-separator env start))
Richard M. Stallman's avatar
Richard M. Stallman committed
655 656 657 658 659
	  (setq dir (substring env start match) start (1+ match))
	  ;;(and (file-directory-p dir) (not (member dir ret)) ...)
	  (setq ret (cons dir ret)))
	(setq ret (nreverse ret))
	(and empty (setq match (member "" ret))
660
	     (progn			; allow string or list here
Richard M. Stallman's avatar
Richard M. Stallman committed
661 662 663 664 665
	       (setcdr match (append (cdr-safe empty) (cdr match)))
	       (setcar match (or (car-safe empty) empty))))
	ret)))

(defun ffap-reduce-path (path)
666
  "Remove duplicates and non-directories from PATH list."
Richard M. Stallman's avatar
Richard M. Stallman committed
667 668 669
  (let (ret tem)
    (while path
      (setq tem path path (cdr path))
670
      (if (equal (car tem) ".") (setcar tem ""))
Richard M. Stallman's avatar
Richard M. Stallman committed
671 672 673 674 675
      (or (member (car tem) ret)
	  (not (file-directory-p (car tem)))
	  (progn (setcdr tem ret) (setq ret tem))))
    (nreverse ret)))

676
(defun ffap-all-subdirs (dir &optional depth)
Juanma Barranquero's avatar
Juanma Barranquero committed
677
  "Return list of all subdirectories under DIR, starting with itself.
678 679 680 681 682 683 684 685 686 687 688 689 690 691 692 693 694 695 696 697 698 699 700 701 702 703 704 705 706 707 708 709 710 711 712 713 714 715 716
Directories beginning with \".\" are ignored, and directory symlinks
are listed but never searched (to avoid loops).
Optional DEPTH limits search depth."
  (and (file-exists-p dir)
       (ffap-all-subdirs-loop (expand-file-name dir) (or depth -1))))

(defun ffap-all-subdirs-loop (dir depth) ; internal
  (setq depth (1- depth))
  (cons dir
	(and (not (eq depth -1))
	     (apply 'nconc
		    (mapcar
		     (function
		      (lambda (d)
			(cond
			 ((not (file-directory-p d)) nil)
			 ((file-symlink-p d) (list d))
			 (t (ffap-all-subdirs-loop d depth)))))
		     (directory-files dir t "\\`[^.]")
		     )))))

(defvar ffap-kpathsea-depth 1
  "Bound on depth of subdirectory search in `ffap-kpathsea-expand-path'.
Set to 0 to avoid all searching, or nil for no limit.")

(defun ffap-kpathsea-expand-path (path)
  "Replace each \"//\"-suffixed dir in PATH by a list of its subdirs.
The subdirs begin with the original directory, and the depth of the
search is bounded by `ffap-kpathsea-depth'.  This is intended to mimic
kpathsea, a library used by some versions of TeX."
  (apply 'nconc
	 (mapcar
	  (function
	   (lambda (dir)
	     (if (string-match "[^/]//\\'" dir)
		 (ffap-all-subdirs (substring dir 0 -2) ffap-kpathsea-depth)
	       (list dir))))
	  path)))

Stefan Monnier's avatar
Stefan Monnier committed
717
(defun ffap-locate-file (file nosuffix path)
Richard M. Stallman's avatar
Richard M. Stallman committed
718
  ;; The current version of locate-library could almost replace this,
719
  ;; except it does not let us override the suffix list.  The
720
  ;; compression-suffixes search moved to ffap-file-exists-string.
Stefan Monnier's avatar
Stefan Monnier committed
721 722
  "A generic path-searching function.
Returns the name of file in PATH, or nil.
723
Optional NOSUFFIX, if nil or t, is like the fourth argument
Stefan Monnier's avatar
Stefan Monnier committed
724
for `load': whether to try the suffixes (\".elc\" \".el\" \"\").
725
If a nonempty list, it is a list of suffixes to try instead.
Stefan Monnier's avatar
Stefan Monnier committed
726
PATH is a list of directories.
727

Stefan Monnier's avatar
Stefan Monnier committed
728
This uses `ffap-file-exists-string', which may try adding suffixes from
729
`ffap-compression-suffixes'."
730 731 732
  (if (file-name-absolute-p file)
      (setq path (list (file-name-directory file))
	    file (file-name-nondirectory file)))
Stefan Monnier's avatar
Stefan Monnier committed
733 734
  (let ((dir-ok (equal "" (file-name-nondirectory file)))
        (suffixes-to-try
735 736 737
	 (cond
	  ((consp nosuffix) nosuffix)
	  (nosuffix '(""))
738 739 740 741 742 743 744 745 746 747 748 749 750
	  (t '(".elc" ".el" ""))))
	suffixes try found)
    (while path
      (setq suffixes suffixes-to-try)
      (while suffixes
	(setq try (ffap-file-exists-string
		   (expand-file-name
		    (concat file (car suffixes)) (car path))))
	(if (and try (or dir-ok (not (file-directory-p try))))
	    (setq found try suffixes nil path nil)
	  (setq suffixes (cdr suffixes))))
      (setq path (cdr path)))
    found))
751 752 753 754 755 756 757 758


;;; Action List (`ffap-alist'):
;;
;; These search actions depend on the major-mode or regexps matching
;; the current name.  The little functions and their variables are
;; deferred to the next section, at some loss of "code locality".  A
;; good example of featuritis.  Trim this list for speed.
Richard M. Stallman's avatar
Richard M. Stallman committed
759 760

(defvar ffap-alist
761
  '(
762 763 764
    ("" . ffap-completable)		; completion, slow on some systems
    ("\\.info\\'" . ffap-info)		; gzip.info
    ("\\`info/" . ffap-info-2)		; info/emacs
765
    ("\\`[-[:lower:]]+\\'" . ffap-info-3) ; (emacs)Top [only in the parentheses]
766 767
    ("\\.elc?\\'" . ffap-el)		; simple.el, simple.elc
    (emacs-lisp-mode . ffap-el-mode)	; rmail, gnus, simple, custom
768
    ;; (lisp-interaction-mode . ffap-el-mode) ; maybe
769 770
    (finder-mode . ffap-el-mode)	; type {C-h p} and try it
    (help-mode . ffap-el-mode)		; maybe useful
771
    (c++-mode . ffap-c++-mode)         ; search ffap-c++-path
772 773 774 775 776 777
    (cc-mode . ffap-c-mode)		; same
    ("\\.\\([chCH]\\|cc\\|hh\\)\\'" . ffap-c-mode) ; stdio.h
    (fortran-mode . ffap-fortran-mode)	; FORTRAN requested by MDB
    ("\\.[fF]\\'" . ffap-fortran-mode)
    (tex-mode . ffap-tex-mode)		; search ffap-tex-path
    (latex-mode . ffap-latex-mode)	; similar
778
    ("\\.\\(tex\\|sty\\|doc\\|cls\\)\\'" . ffap-tex)
779 780 781
    ("\\.bib\\'" . ffap-bib)		; search ffap-bib-path
    ("\\`\\." . ffap-home)		; .emacs, .bashrc, .profile
    ("\\`~/" . ffap-lcd)		; |~/misc/ffap.el.Z|
Stefan Monnier's avatar
Stefan Monnier committed
782
    ;; This used to have a blank, but ffap-string-at-point doesn't
783 784
    ;; handle blanks.
    ;; http://lists.gnu.org/archive/html/emacs-devel/2008-01/msg01058.html
Stefan Monnier's avatar
Stefan Monnier committed
785
    ("\\`[Rr][Ff][Cc][-#]?\\([0-9]+\\)"	; no $
786 787 788
     . ffap-rfc)			; "100% RFC2100 compliant"
    (dired-mode . ffap-dired)		; maybe in a subdirectory
    )
Juanma Barranquero's avatar
Juanma Barranquero committed
789
  "Alist of (KEY . FUNCTION) pairs parsed by `ffap-file-at-point'.
Juanma Barranquero's avatar
Juanma Barranquero committed
790
If string NAME at point (maybe \"\") is not a file or URL, these pairs
791 792
specify actions to try creating such a string.  A pair matches if either
  KEY is a symbol, and it equals `major-mode', or
Juanma Barranquero's avatar
Juanma Barranquero committed
793
  KEY is a string, it should match NAME as a regexp.
Juanma Barranquero's avatar
Juanma Barranquero committed
794
On a match, (FUNCTION NAME) is called and should return a file, an
795 796 797 798
URL, or nil.  If nil, search the alist for further matches.
While calling FUNCTION, the match data is set according to KEY if KEY
is a string, so that FUNCTION can use `match-string' and friends
to extract substrings.")
799

Richard M. Stallman's avatar
Richard M. Stallman committed
800
(put 'ffap-alist 'risky-local-variable t)
801

802 803 804 805 806 807 808 809 810 811 812 813 814 815 816
;; Example `ffap-alist' modifications:
;;
;; (setq ffap-alist                   ; remove a feature in `ffap-alist'
;;	 (delete (assoc 'c-mode ffap-alist) ffap-alist))
;;
;; (setq ffap-alist                   ; add something to `ffap-alist'
;;	 (cons
;;	  (cons "^YSN[0-9]+$"
;;		(defun ffap-ysn (name)
;;		  (concat
;;		   "http://www.physics.uiuc.edu/"
;;                 "ysn/httpd/htdocs/ysnarchive/issuefiles/"
;;		   (substring name 3) ".html")))
;;	  ffap-alist))

817

818 819 820 821 822 823 824 825 826 827
;;; Action Definitions:
;;
;; Define various default members of `ffap-alist'.

(defun ffap-completable (name)
  (let* ((dir (or (file-name-directory name) default-directory))
	 (cmp (file-name-completion (file-name-nondirectory name) dir)))
    (and cmp (concat dir cmp))))

(defun ffap-home (name) (ffap-locate-file name t '("~")))
828 829

(defun ffap-info (name)
830
  (ffap-locate-file
831
   name '("" ".info")
Stefan Monnier's avatar
Stefan Monnier committed
832 833
   (or (ffap-symbol-value 'Info-directory-list)
       (ffap-symbol-value 'Info-default-directory-list)
834
       )))
835 836 837 838

(defun ffap-info-2 (name) (ffap-info (substring name 5)))

(defun ffap-info-3 (name)
839
  ;; This ignores the node! "(emacs)Top" same as "(emacs)Intro"
840 841
  (and (equal (ffap-string-around) "()") (ffap-info name)))

Stefan Monnier's avatar
Stefan Monnier committed
842
(defun ffap-el (name) (ffap-locate-file name t load-path))
843 844

(defun ffap-el-mode (name)
845 846
  ;; If name == "foo.el" we will skip it, since ffap-el already
  ;; searched for it once.  (This assumes the default ffap-alist.)
847
  (and (not (string-match "\\.el\\'" name))
Stefan Monnier's avatar
Stefan Monnier committed
848
       (ffap-locate-file name '(".el") load-path)))
849

850 851 852 853 854
;; 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
855
(defvar ffap-c-path
856 857 858 859 860 861 862 863 864 865 866 867
  (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.")

868 869 870
(defun ffap-c-mode (name)
  (ffap-locate-file name t ffap-c-path))

871 872 873 874 875 876 877 878 879 880 881 882 883 884 885 886 887 888 889 890 891 892
(defvar ffap-c++-path
  (let ((c++-include-dir (with-temp-buffer
                           (when (eq 0 (ignore-errors
                                         (call-process "g++" nil t nil "-v")))
                             (goto-char (point-min))
                             (if (re-search-forward "--with-gxx-include-dir=\
\\([^[:space:]]+\\)"
                                                      nil 'noerror)
                                 (match-string 1)
                               (when (re-search-forward "gcc version \
\\([[:digit:]]+.[[:digit:]]+.[[:digit:]]+\\)"
                                                   nil 'noerror)
                                 (expand-file-name (match-string 1)
                                                   "/usr/include/c++/")))))))
    (if c++-include-dir
        (cons c++-include-dir ffap-c-path)
      ffap-c-path))
  "List of directories to search for include files.")

(defun ffap-c++-mode (name)
  (ffap-locate-file name t ffap-c++-path))

893 894 895 896
(defvar ffap-fortran-path '("../include" "/usr/include"))

(defun ffap-fortran-mode (name)
  (ffap-locate-file name t ffap-fortran-path))
897 898 899

(defvar ffap-tex-path
  t				; delayed initialization
Juanma Barranquero's avatar
Juanma Barranquero committed
900
  "Path where `ffap-tex-mode' looks for TeX files.
901
If t, `ffap-tex-init' will initialize this when needed.")
Richard M. Stallman's avatar
Richard M. Stallman committed
902

903 904 905 906 907 908 909 910 911 912 913 914 915 916 917 918 919 920
(defvar ffap-latex-guess-rules '(("" . ".sty")
                               ("" . ".cls")
                               ("" . ".ltx")
                               ("" . ".tex")
                               ("" . "") ;; in some rare cases the
                                         ;; extension is already in
                                         ;; the buffer.
                               ("beamertheme" . ".sty")
                               ("beamercolortheme". ".sty")
                               ("beamerfonttheme". ".sty")
                               ("beamerinnertheme". ".sty")
                               ("beameroutertheme". ".sty")
                               ("" . ".ldf"))
  "List of rules for guessing a filename.
Each rule is a cons (PREFIX . SUFFIX) used for guessing a
filename from the word at point by prepending PREFIX and
appending SUFFIX.")

Stefan Monnier's avatar
Stefan Monnier committed
921
(defun ffap-tex-init ()
922 923
  ;; Compute ffap-tex-path if it is now t.
  (and (eq t ffap-tex-path)
924
       ;; this may be slow, so say something
925 926 927
       (message "Initializing ffap-tex-path ...")
       (setq ffap-tex-path
	     (ffap-reduce-path
928 929 930 931 932 933
	      (cons
	       "."
	       (ffap-kpathsea-expand-path
		(append
		 (ffap-list-env "TEXINPUTS")
		 ;; (ffap-list-env "BIBINPUTS")
Stefan Monnier's avatar
Stefan Monnier committed
934 935
		 (ffap-symbol-value
		  'TeX-macro-global	; AUCTeX