Add `extensions' to keywords, since it's internal.
[bpt/emacs.git] / lisp / net / browse-url.el
CommitLineData
8749abea
GM
1;;; browse-url.el --- Pass a URL to a WWW browser
2
3;; Copyright (C) 1995, 96, 97, 98, 99, 2000 Free Software Foundation, Inc.
4
5;; Author: Denis Howe <dbh@doc.ic.ac.uk>
6;; Maintainer: Dave Love <fx@gnu.org>
7;; Created: 03 Apr 1995
8;; Keywords: hypertext, hypermedia, mouse
9
10;; This file is part of GNU Emacs.
11
12;; GNU Emacs is free software; you can redistribute it and/or modify
13;; it under the terms of the GNU General Public License as published by
14;; the Free Software Foundation; either version 2, or (at your option)
15;; any later version.
16
17;; GNU Emacs is distributed in the hope that it will be useful,
18;; but WITHOUT ANY WARRANTY; without even the implied warranty of
19;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
20;; GNU General Public License for more details.
21
22;; You should have received a copy of the GNU General Public License
23;; along with GNU Emacs; see the file COPYING. If not, write to the
24;; Free Software Foundation, Inc., 59 Temple Place - Suite 330,
25;; Boston, MA 02111-1307, USA.
26
27;;; Commentary:
28
29;; This package provides functions which read a URL (Uniform Resource
30;; Locator) from the minibuffer, defaulting to the URL around point,
31;; and ask a World-Wide Web browser to load it. It can also load the
32;; URL associated with the current buffer. Different browsers use
33;; different methods of remote control so there is one function for
34;; each supported browser. If the chosen browser is not running, it
3abc9fa1
DL
35;; is started. Currently there is support for the following browsers,
36;; some of them probably now obsolete:
37
38;; Function Browser Earliest version
39;; browse-url-netscape Netscape 1.1b1
40;; browse-url-mosaic XMosaic/mMosaic <= 2.4
41;; browse-url-cci XMosaic 2.5
42;; browse-url-w3 w3 0
43;; browse-url-w3-gnudoit w3 remotely
44;; browse-url-iximosaic IXI Mosaic ?
45;; browse-url-lynx-* Lynx 0
46;; browse-url-grail Grail 0.3b1
47;; browse-url-mmm MMM ?
48;; browse-url-generic arbitrary
49;; browse-url-default-windows-browser MS-Windows browser
8749abea
GM
50
51;; [A version of the Netscape browser is now free software
52;; <URL:http://www.mozilla.org/>, albeit not GPLed, so it is
53;; reasonable to have that as the default.]
54
55;; Note that versions of Netscape before 1.1b1 did not have remote
56;; control. <URL:http://www.netscape.com/newsref/std/x-remote.html>.
57
58;; Browsers can cache Web pages so it may be necessary to tell them to
59;; reload the current page if it has changed (e.g. if you have edited
60;; it). There is currently no perfect automatic solution to this.
61
62;; Netscape allows you to specify the id of the window you want to
63;; control but which window DO you want to control and how do you
64;; discover its id?
65
66;; If using XMosaic before version 2.5, check the definition of
67;; browse-url-usr1-signal below.
68;; <URL:http://www.ncsa.uiuc.edu/SDG/Software/XMosaic/remote-control.html>
69
70;; XMosaic version 2.5 introduced Common Client Interface allowing you
71;; to control mosaic through Unix sockets.
72;; <URL:http://www.ncsa.uiuc.edu/SDG/Software/XMosaic/CCI/cci-spec.html>
73
74;; William M. Perry's excellent "w3" WWW browser for
75;; Emacs <URL:ftp://cs.indiana.edu/pub/elisp/w3/>
76;; has a function w3-follow-url-at-point, but that
77;; doesn't let you edit the URL like browse-url.
78;; The `gnuserv' package that can be used to control it in another
79;; Emacs process is available from
80;; <URL:ftp://ftp.splode.com/pub/users/friedman/packages/>.
81
82;; Grail is the freely available WWW browser implemented in Python, a
83;; cool object-oriented freely available interpreted language. Grail
84;; 0.3b1 was the first version to have remote control as distributed.
85;; For more information on Grail see
86;; <URL:http://grail.cnri.reston.va.us/> and for more information on
87;; Python see <url:http://www.python.org/>. Grail support in
88;; browse-url.el written by Barry Warsaw <bwarsaw@python.org>.
89
90;; MMM is a semi-free WWW browser implemented in Objective Caml, an
91;; interesting impure functional programming language. See
92;; <URL:http://pauillac.inria.fr/%7Erouaix/mmm/>.
93
94;; Lynx is now distributed by the FSF. See also
95;; <URL:http://lynx.browser.org/>.
96
97;; Free graphical browsers that could be used by `browse-url-generic'
98;; include Chimera <URL:ftp://ftp.cs.unlv.edu/pub/chimera> and
99;; <URL:http://www.unlv.edu/chimera/>, Arena
100;; <URL:ftp://ftp.yggdrasil.com/pub/dist/web/arena> and Amaya
101;; <URL:ftp://ftp.w3.org/pub/amaya>. mMosaic
102;; <URL:ftp://sig.enst.fr/pub/multicast/mMosaic/>,
103;; <URL:http://sig.enst.fr/~dauphin/mMosaic/> (with development
104;; support for Java applets and multicast) can be used like Mosaic by
105;; setting `browse-url-mosaic-program' appropriately.
106
107;; I [Denis Howe, not Dave Love] recommend Nelson Minar
108;; <nelson@santafe.edu>'s excellent html-helper-mode.el for editing
109;; HTML and thank Nelson for his many useful comments on this code.
110;; <URL:http://www.santafe.edu/%7Enelson/hhm-beta/>
111
112;; See also hm--html-menus <URL:http://www.tnt.uni-hannover.de/%7Emuenkel/
113;; software/own/hm--html-menus/>. For composing correct HTML see also
114;; PSGML the general SGML structure editor package
115;; <URL:ftp://ftp.lysator.liu.se/pub/sgml>; hm--html-menus can be used
116;; with this.
117
118;; This package generalises function html-previewer-process in Marc
119;; Andreessen's html-mode (LCD modes/html-mode.el.Z). See also the
120;; ffap.el package. The huge hyperbole package also contains similar
121;; functions.
122
123;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
124;; Help!
125
126;; Can you write and test some code for the Macintrash and Windoze
127;; Netscape remote control APIs? (See the URL above).
128
129;; Do any other browsers have remote control?
130
131;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
132;; Usage
133
134;; To display the URL at or before point:
135;; M-x browse-url-at-point RET
136;; or, similarly but with the opportunity to edit the URL extracted from
137;; the buffer, use:
138;; M-x browse-url
139
140;; To display a URL by shift-clicking on it, put this in your ~/.emacs
141;; file:
142;; (global-set-key [S-mouse-2] 'browse-url-at-mouse)
143;; (Note that using Shift-mouse-1 is not desirable because
144;; that event has a standard meaning in Emacs.)
145
146;; To display the current buffer in a web browser:
147;; M-x browse-url-of-buffer RET
148
149;; To display the current region in a web browser:
150;; M-x browse-url-of-region RET
151
152;; In Dired, to display the file named on the current line:
153;; M-x browse-url-of-dired-file RET
154
155;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
156;; Customisation (~/.emacs)
157
158;; To see what variables are available for customization, type
159;; `M-x set-variable browse-url TAB'. Better, use
160;; `M-x customize-group browse-url'.
161
162;; Bind the browse-url commands to keys with the `C-c C-z' prefix
163;; (as used by html-helper-mode):
164;; (global-set-key "\C-c\C-z." 'browse-url-at-point)
165;; (global-set-key "\C-c\C-zb" 'browse-url-of-buffer)
166;; (global-set-key "\C-c\C-zr" 'browse-url-of-region)
167;; (global-set-key "\C-c\C-zu" 'browse-url)
168;; (global-set-key "\C-c\C-zv" 'browse-url-of-file)
169;; (add-hook 'dired-mode-hook
170;; (lambda ()
171;; (local-set-key "\C-c\C-zf" 'browse-url-of-dired-file)))
172
3abc9fa1 173;; Browse URLs in mail messages under RMAIL by clicking mouse-2:
8749abea
GM
174;; (add-hook 'rmail-mode-hook (lambda () ; rmail-mode startup
175;; (define-key rmail-mode-map [mouse-2] 'browse-url-at-mouse)))
3abc9fa1 176;; Alternatively, add `goto-address' to `rmail-show-message-hook'.
8749abea 177
3abc9fa1
DL
178;; Gnus provides a standard feature to activate URLs in article
179;; buffers for invocation of browse-url.
8749abea
GM
180
181;; Use the Emacs w3 browser when not running under X11:
182;; (or (eq window-system 'x)
183;; (setq browse-url-browser-function 'browse-url-w3))
184
185;; To always save modified buffers before displaying the file in a browser:
186;; (setq browse-url-save-file t)
187
188;; To get round the Netscape caching problem, you could EITHER have
189;; write-file in html-helper-mode make Netscape reload the document:
190;;
191;; (autoload 'browse-url-netscape-reload "browse-url"
192;; "Ask a WWW browser to redisplay the current file." t)
193;; (add-hook 'html-helper-mode-hook
194;; (lambda ()
195;; (add-hook 'local-write-file-hooks
196;; (lambda ()
197;; (let ((local-write-file-hooks))
198;; (save-buffer))
199;; (browse-url-netscape-reload)
200;; t) ; => file written by hook
201;; t))) ; append to l-w-f-hooks
202;;
203;; OR have browse-url-of-file ask Netscape to load and then reload the
204;; file:
205;;
206;; (add-hook 'browse-url-of-file-hook 'browse-url-netscape-reload)
207
208;; You may also want to customise browse-url-netscape-arguments, e.g.
209;; (setq browse-url-netscape-arguments '("-install"))
210;;
211;; or similarly for the other browsers.
212
213;; To invoke different browsers for different URLs:
214;; (setq browse-url-browser-function '(("^mailto:" . browse-url-mail)
215;; ("." . browse-url-netscape)))
216
217;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
218;;; Code:
219
220;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
221;; Variables
222
223(eval-when-compile (require 'thingatpt)
224 (require 'term)
225 (require 'dired)
226 (require 'w3-auto nil t))
227
228(defgroup browse-url nil
229 "Use a web browser to look at a URL."
230 :prefix "browse-url-"
082527fe 231 :link '(emacs-commentary-link "browse-url")
8749abea
GM
232 :group 'hypermedia)
233
234;;;###autoload
235(defcustom browse-url-browser-function
236 (if (eq system-type 'windows-nt)
237 'browse-url-default-windows-browser
238 'browse-url-netscape)
239 "*Function to display the current buffer in a WWW browser.
240This is used by the `browse-url-at-point', `browse-url-at-mouse', and
241`browse-url-of-file' commands.
242
243If the value is not a function it should be a list of pairs
082527fe 244\(REGEXP . FUNCTION). In this case the function called will be the one
8749abea
GM
245associated with the first REGEXP which matches the current URL. The
246function is passed the URL and any other args of `browse-url'. The last
247regexp should probably be \".\" to specify a default browser."
248 :type '(choice
082527fe
DL
249 (function-item :tag "Emacs W3" :value browse-url-w3)
250 (function-item :tag "W3 in another Emacs via `gnudoit'"
251 :value browse-url-w3-gnudoit)
252 (function-item :tag "Netscape" :value browse-url-netscape)
253 (function-item :tag "Mosaic" :value browse-url-mosaic)
254 (function-item :tag "Mosaic using CCI" :value browse-url-cci)
255 (function-item :tag "IXI Mosaic" :value browse-url-iximosaic)
256 (function-item :tag "Lynx in an xterm window"
257 :value browse-url-lynx-xterm)
258 (function-item :tag "Lynx in an Emacs window"
259 :value browse-url-lynx-emacs)
260 (function-item :tag "Grail" :value browse-url-grail)
261 (function-item :tag "MMM" :value browse-url-mmm)
262 (function-item :tag "Specified by `Browse Url Generic Program'"
263 :value browse-url-generic)
264 (function-item :tag "Default Windows browser"
265 :value browse-url-default-windows-browser)
266 (function :tag "Your own function")
267 (alist :tag "Regexp/function association list"
268 :key-type regexp :value-type function))
8749abea
GM
269 :version "20.4"
270 :group 'browse-url)
271
272(defcustom browse-url-netscape-program "netscape"
273 ;; Info about netscape-remote from Karl Berry.
274 "The name by which to invoke Netscape.
275
276The free program `netscape-remote' from
277<URL:http://home.netscape.com/newsref/std/remote.c> is said to start
278up very much quicker than `netscape'. Reported to compile on a GNU
279system, given vroot.h from the same directory, with cc flags
280 -DSTANDALONE -L/usr/X11R6/lib -lXmu -lX11."
281 :type 'string
282 :group 'browse-url)
283
284(defcustom browse-url-netscape-arguments nil
285 "A list of strings to pass to Netscape as arguments."
286 :type '(repeat (string :tag "Argument"))
287 :group 'browse-url)
288
289(defcustom browse-url-netscape-startup-arguments browse-url-netscape-arguments
290 "A list of strings to pass to Netscape when it starts up.
291Defaults to the value of `browse-url-netscape-arguments' at the time
292`browse-url' is loaded."
293 :type '(repeat (string :tag "Argument"))
294 :group 'browse-url)
295
296;;;###autoload
297(defcustom browse-url-new-window-p nil
298 "*If non-nil, always open a new browser window with appropriate browsers.
299Passing an interactive argument to \\[browse-url], or specific browser
300commands reverses the effect of this variable. Requires Netscape version
3011.1N or later or XMosaic version 2.5 or later if using those browsers."
302 :type 'boolean
303 :group 'browse-url)
304
305;;;###autoload
306(defcustom browse-url-netscape-display nil
307 "*The X display for running Netscape, if not same as Emacs'."
308 :type '(choice string (const :tag "Default" nil))
309 :group 'browse-url)
310
311(defcustom browse-url-mosaic-program "xmosaic"
312 "The name by which to invoke Mosaic (or mMosaic)."
313 :type 'string
314 :version "20.3"
315 :group 'browse-url)
316
317(defcustom browse-url-mosaic-arguments nil
318 "A list of strings to pass to Mosaic as arguments."
319 :type '(repeat (string :tag "Argument"))
320 :group 'browse-url)
321
322(defcustom browse-url-filename-alist
3abc9fa1 323 `(("^/\\(ftp@\\|anonymous@\\)?\\([^:]+\\):/*" . "ftp://\\2/")
8749abea
GM
324 ;; The above loses the username to avoid the browser prompting for
325 ;; it in anonymous cases. If it's not anonymous the next regexp
326 ;; applies.
327 ("^/\\([^:@]+@\\)?\\([^:]+\\):/*" . "ftp://\\1\\2/")
3abc9fa1
DL
328 ,@(if (memq system-type '(windows-nt ms-dos))
329 '("^\\([a-zA-Z]:\\)[\\/]" . "file:\\1/"))
8749abea
GM
330 ("^/+" . "file:/"))
331 "An alist of (REGEXP . STRING) pairs used by `browse-url-of-file'.
332Any substring of a filename matching one of the REGEXPs is replaced by
333the corresponding STRING using `replace-match', not treating STRING
334literally. All pairs are applied in the order given. The default
335value converts ange-ftp/EFS-style paths into ftp URLs and prepends
336`file:' to any path beginning with `/'.
337
338For example, adding to the default a specific translation of an ange-ftp
339address to an HTTP URL:
340
341 (setq browse-url-filename-alist
342 '((\"/webmaster@webserver:/home/www/html/\" .
343 \"http://www.acme.co.uk/\")
344 (\"^/\\(ftp@\\|anonymous@\\)?\\([^:]+\\):/*\" . \"ftp://\\2/\")
345 (\"^/\\([^:@]+@\\)?\\([^:]+\\):/*\" . \"ftp://\\1\\2/\")
346 (\"^/+\" . \"file:/\")))
347"
348 :type '(repeat (cons :format "%v"
349 (regexp :tag "Regexp")
350 (string :tag "Replacement")))
351 :version "20.3"
352 :group 'browse-url)
353
354;;;###autoload
355(defcustom browse-url-save-file nil
356 "*If non-nil, save the buffer before displaying its file.
357Used by the `browse-url-of-file' command."
358 :type 'boolean
359 :group 'browse-url)
360
361(defcustom browse-url-of-file-hook nil
362 "Run after `browse-url-of-file' has asked a browser to load a file.
363
364Set this to `browse-url-netscape-reload' to force Netscape to load the
365file rather than displaying a cached copy."
366 :type 'hook
367 :options '(browse-url-netscape-reload)
368 :group 'browse-url)
369
370(defvar browse-url-usr1-signal
371 (if (and (boundp 'emacs-major-version)
372 (or (> emacs-major-version 19) (>= emacs-minor-version 29)))
373 'SIGUSR1 ; Why did I think this was in lower case before?
374 30) ; Check /usr/include/signal.h.
375 "The argument to `signal-process' for sending SIGUSR1 to XMosaic.
376Emacs 19.29 accepts 'SIGUSR1, earlier versions require an integer
377which is 30 on SunOS and 16 on HP-UX and Solaris.")
378
379(defcustom browse-url-CCI-port 3003
380 "Port to access XMosaic via CCI.
381This can be any number between 1024 and 65535 but must correspond to
382the value set in the browser."
383 :type 'integer
384 :group 'browse-url)
385
386(defcustom browse-url-CCI-host "localhost"
387 "*Host to access XMosaic via CCI.
388This should be the host name of the machine running XMosaic with CCI
389enabled. The port number should be set in `browse-url-CCI-port'."
390 :type 'string
391 :group 'browse-url)
392
393(defvar browse-url-temp-file-name nil)
394(make-variable-buffer-local 'browse-url-temp-file-name)
395
396(defcustom browse-url-xterm-program "xterm"
397 "The name of the terminal emulator used by `browse-url-lynx-xterm'.
398This might, for instance, be a separate colour version of xterm."
399 :type 'string
400 :group 'browse-url)
401
402(defcustom browse-url-xterm-args nil
403 "*A list of strings defining options for `browse-url-xterm-program'.
404These might set its size, for instance."
405 :type '(repeat (string :tag "Argument"))
406 :group 'browse-url)
407
408(defcustom browse-url-lynx-emacs-args (and (not window-system)
409 '("-show_cursor"))
410 "A list of strings defining options for Lynx in an Emacs buffer.
411
412The default is none in a window system, otherwise `-show_cursor' to
413indicate the position of the current link in the absence of
414highlighting, assuming the normal default for showing the cursor."
415 :type '(repeat (string :tag "Argument"))
416 :version "20.3"
417 :group 'browse-url)
418
419(defcustom browse-url-gnudoit-program "gnudoit"
420 "The name of the `gnudoit' program used by `browse-url-w3-gnudoit'."
421 :type 'string
422 :group 'browse-url)
423
424(defcustom browse-url-gnudoit-args '("-q")
425 "*A list of strings defining options for `browse-url-gnudoit-program'.
426These might set the port, for instance."
427 :type '(repeat (string :tag "Argument"))
428 :group 'browse-url)
429
430;;;###autoload
431(defcustom browse-url-generic-program nil
432 "*The name of the browser program used by `browse-url-generic'."
433 :type '(choice string (const :tag "None" nil))
434 :group 'browse-url)
435
436(defcustom browse-url-generic-args nil
437 "*A list of strings defining options for `browse-url-generic-program'."
438 :type '(repeat (string :tag "Argument"))
439 :group 'browse-url)
440
441(defcustom browse-url-temp-dir temporary-file-directory
442 "The name of a directory for browse-url's temporary files.
443Such files are generated by functions like `browse-url-of-region'.
444You might want to set this to somewhere with restricted read permissions
445for privacy's sake."
446 :type 'string
447 :group 'browse-url)
448
449(defcustom browse-url-netscape-version
450 3
451 "The version of Netscape you are using.
452This affects how URL reloading is done; the mechanism changed
453incompatibly at version 4."
454 :type 'number
455 :group 'browse-url)
456
457(defcustom browse-url-lynx-input-field 'avoid
458 "*Action on selecting an existing Lynx buffer at an input field.
459What to do when sending a new URL to an existing Lynx buffer in Emacs
460if the Lynx cursor is on an input field (in which case the `g' command
461would be entered as data). Such fields are recognized by the
462underlines ____. Allowed values: nil: disregard it, 'warn: warn the
463user and don't emit the URL, 'avoid: try to avoid the field by moving
464down (this *won't* always work)."
465 :type '(choice (const :tag "Move to try to avoid field" :value avoid)
466 (const :tag "Disregard" :value nil)
467 (const :tag "Warn, don't emit URL" :value warn))
468 :version "20.3"
469 :group 'browse-url)
470
471(defvar browse-url-lynx-input-attempts 10
472 "*How many times to try to move down from a series of lynx input fields.")
473
474(defcustom browse-url-lynx-input-delay 0.2
475 "How many seconds to wait for lynx between moves down from an input field.")
476
477;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
478;; URL input
479
480(defun browse-url-url-at-point ()
481 (let ((url (thing-at-point 'url)))
482 (set-text-properties 0 (length url) nil url)
483 url))
484
485;; Having this as a separate function called by the browser-specific
486;; functions allows them to be stand-alone commands, making it easier
487;; to switch between browsers.
488
489(defun browse-url-interactive-arg (prompt)
490 "Read a URL from the minibuffer, prompting with PROMPT.
491Default to the URL at or before point. If invoked with a mouse button,
492set point to the position clicked first. Return a list for use in
493`interactive' containing the URL and `browse-url-new-window-p' or its
494negation if a prefix argument was given."
495 (let ((event (elt (this-command-keys) 0)))
496 (and (listp event) (mouse-set-point event)))
497 (list (read-string prompt (browse-url-url-at-point))
498 (not (eq (null browse-url-new-window-p)
499 (null current-prefix-arg)))))
500
01f97560
DL
501;; interactive-p needs to be called at a function's top-level, hence
502;; the macro.
503(defmacro browse-url-maybe-new-window (arg)
504 `(if (interactive-p)
505 ,arg
506 browse-url-new-window-p))
507
8749abea
GM
508;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
509;; Browse current buffer
510
511;;;###autoload
512(defun browse-url-of-file (&optional file)
513 "Ask a WWW browser to display FILE.
514Display the current buffer's file if FILE is nil or if called
515interactively. Turn the filename into a URL with function
516`browse-url-file-url'. Pass the URL to a browser using the
517`browse-url' function then run `browse-url-of-file-hook'."
518 (interactive)
519 (or file
520 (setq file (buffer-file-name))
521 (error "Current buffer has no file"))
522 (let ((buf (get-file-buffer file)))
523 (if buf
524 (save-excursion
525 (set-buffer buf)
526 (cond ((not (buffer-modified-p)))
527 (browse-url-save-file (save-buffer))
528 (t (message "%s modified since last save" file))))))
529 (browse-url (browse-url-file-url file))
530 (run-hooks 'browse-url-of-file-hook))
531
532(defun browse-url-file-url (file)
533 "Return the URL corresponding to FILE.
534Use variable `browse-url-filename-alist' to map filenames to URLs."
535 ;; URL-encode special chars, do % first
536 (let ((s 0))
537 (while (setq s (string-match "%" file s))
538 (setq file (replace-match "%25" t t file)
539 s (1+ s))))
540 (while (string-match "[*\"()',=;? ]" file)
541 (let ((enc (format "%%%x" (aref file (match-beginning 0)))))
542 (setq file (replace-match enc t t file))))
543 (let ((maps browse-url-filename-alist))
544 (while maps
545 (let* ((map (car maps))
546 (from-re (car map))
547 (to-string (cdr map)))
548 (setq maps (cdr maps))
549 (and (string-match from-re file)
550 (setq file (replace-match to-string t nil file))))))
551 file)
552
553;;;###autoload
554(defun browse-url-of-buffer (&optional buffer)
555 "Ask a WWW browser to display BUFFER.
556Display the current buffer if BUFFER is nil. Display only the
557currently visible part of BUFFER (from a temporary file) if buffer is
558narrowed."
559 (interactive)
560 (save-excursion
561 (and buffer (set-buffer buffer))
562 (let ((file-name
563 ;; Ignore real name if restricted
564 (and (= (- (point-max) (point-min)) (buffer-size))
565 (or buffer-file-name
566 (and (boundp 'dired-directory) dired-directory)))))
567 (or file-name
568 (progn
569 (or browse-url-temp-file-name
570 (setq browse-url-temp-file-name
571 (convert-standard-filename
572 (make-temp-file
573 (expand-file-name "burl" browse-url-temp-dir)))))
574 (setq file-name browse-url-temp-file-name)
575 (write-region (point-min) (point-max) file-name nil 'no-message)))
576 (browse-url-of-file file-name))))
577
578(defun browse-url-delete-temp-file (&optional temp-file-name)
579 ;; Delete browse-url-temp-file-name from the file system
580 ;; If optional arg TEMP-FILE-NAME is non-nil, delete it instead
581 (let ((file-name (or temp-file-name browse-url-temp-file-name)))
582 (if (and file-name (file-exists-p file-name))
583 (delete-file file-name))))
584
585(add-hook 'kill-buffer-hook 'browse-url-delete-temp-file)
586
587;;;###autoload
588(defun browse-url-of-dired-file ()
589 "In Dired, ask a WWW browser to display the file named on this line."
590 (interactive)
591 (browse-url-of-file (dired-get-filename)))
592
593;;;###autoload
594(defun browse-url-of-region (min max)
595 "Ask a WWW browser to display the current region."
596 (interactive "r")
597 (save-excursion
598 (save-restriction
599 (narrow-to-region min max)
600 (browse-url-of-buffer))))
601
602;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
603;; Browser-independent commands
604
605;; A generic command to call the current browse-url-browser-function
606
607;;;###autoload
608(defun browse-url (url &rest args)
609 "Ask a WWW browser to load URL.
610Prompts for a URL, defaulting to the URL at or before point. Variable
611`browse-url-browser-function' says which browser to use."
612 (interactive (browse-url-interactive-arg "URL: "))
01f97560 613 (unless (interactive-p)
3abc9fa1 614 (setq args (or args (list browse-url-new-window-p))))
8749abea
GM
615 (if (functionp browse-url-browser-function)
616 (apply browse-url-browser-function url args)
617 ;; The `function' can be an alist; look down it for first match
618 ;; and apply the function (which might be a lambda).
619 (catch 'done
3abc9fa1
DL
620 (dolist (bf browse-url-browser-function)
621 (when (string-match (car bf) url)
622 (apply (cdr bf) url args)
623 (throw 'done t)))
624 (error "No browse-url-browser-function matching URL %s"
8749abea
GM
625 url))))
626
627;;;###autoload
01f97560 628(defun browse-url-at-point (&optional arg)
8749abea
GM
629 "Ask a WWW browser to load the URL at or before point.
630Doesn't let you edit the URL like `browse-url'. Variable
631`browse-url-browser-function' says which browser to use."
01f97560 632 (interactive "P")
3abc9fa1
DL
633 (let ((url (browse-url-url-at-point)))
634 (if url
635 (browse-url url (if arg
636 (not browse-url-new-window-p)
637 browse-url-new-window-p))
638 (error "No URL found"))))
8749abea
GM
639
640;;;###autoload
641(defun browse-url-at-mouse (event)
642 "Ask a WWW browser to load a URL clicked with the mouse.
643The URL is the one around or before the position of the mouse click
644but point is not changed. Doesn't let you edit the URL like
645`browse-url'. Variable `browse-url-browser-function' says which browser
646to use."
647 (interactive "e")
648 (save-excursion
3abc9fa1
DL
649 (mouse-set-point event)
650 (browse-url-at-point browse-url-new-window-p)))
8749abea
GM
651
652;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
653;; Browser-specific commands
654
655;; --- Default MS-Windows browser ---
656
657(defun browse-url-default-windows-browser (url &optional new-window)
658 (interactive (browse-url-interactive-arg "URL: "))
659 (w32-shell-execute "open" url))
660
661;; --- Netscape ---
662
663(defun browse-url-process-environment ()
664 "Set DISPLAY in the environment to the X display Netscape is running on.
665This is either the value of variable `browse-url-netscape-display' if
666non-nil, or the same display as Emacs if different from the current
667environment, otherwise just use the current environment."
668 (let ((display (or browse-url-netscape-display (browse-url-emacs-display))))
669 (if display
670 (cons (concat "DISPLAY=" display) process-environment)
671 process-environment)))
672
673(defun browse-url-emacs-display ()
674 "Return the X display Emacs is running on.
675This is nil if the display is the same as the DISPLAY environment variable.
676
677Actually Emacs could be using several displays; this just returns the
678one showing the selected frame."
679 (let ((display (cdr-safe (assq 'display (frame-parameters)))))
680 (and (not (equal display (getenv "DISPLAY")))
681 display)))
682
683;;;###autoload
684(defun browse-url-netscape (url &optional new-window)
685 "Ask the Netscape WWW browser to load URL.
686
687Default to the URL around or before point. The strings in variable
688`browse-url-netscape-arguments' are also passed to Netscape.
689
690When called interactively, if variable `browse-url-new-window-p' is
691non-nil, load the document in a new Netscape window, otherwise use a
692random existing one. A non-nil interactive prefix argument reverses
693the effect of `browse-url-new-window-p'.
694
695When called non-interactively, optional second argument NEW-WINDOW is
696used instead of `browse-url-new-window-p'."
697 (interactive (browse-url-interactive-arg "Netscape URL: "))
698 ;; URL encode any `confusing' characters in the URL. This needs to
699 ;; include at least commas; presumably also close parens.
700 (while (string-match "[,)]" url)
701 (setq url (replace-match
702 (format "%%%x" (string-to-char (match-string 0 url))) t t url)))
703 (let* ((process-environment (browse-url-process-environment))
704 (process (apply 'start-process
705 (concat "netscape " url) nil
706 browse-url-netscape-program
707 (append
708 browse-url-netscape-arguments
709 (if (eq window-system 'w32)
710 (list url)
711 (append
712 (if new-window '("-noraise"))
713 (list "-remote"
714 (concat "openURL(" url
01f97560
DL
715 (if (browse-url-maybe-new-window
716 new-window)
717 ",new-window")
8749abea
GM
718 ")"))))))))
719 (set-process-sentinel process
3abc9fa1
DL
720 `(lambda (process change)
721 (browse-url-netscape-sentinel process ,url)))))
8749abea
GM
722
723(defun browse-url-netscape-sentinel (process url)
724 "Handle a change to the process communicating with Netscape."
725 (or (eq (process-exit-status process) 0)
726 (let* ((process-environment (browse-url-process-environment)))
727 ;; Netscape not running - start it
728 (message "Starting Netscape...")
729 (apply 'start-process (concat "netscape" url) nil
730 browse-url-netscape-program
731 (append browse-url-netscape-startup-arguments (list url))))))
732
733(defun browse-url-netscape-reload ()
734 "Ask Netscape to reload its current document.
735How depends on `browse-url-netscape-version'."
736 (interactive)
737 ;; Backwards incompatibility reported by
738 ;; <peter.kruse@psychologie.uni-regensburg.de>.
739 (browse-url-netscape-send (if (>= browse-url-netscape-version 4)
740 "xfeDoCommand(reload)"
741 "reload")))
742
743(defun browse-url-netscape-send (command)
744 "Send a remote control command to Netscape."
745 (let* ((process-environment (browse-url-process-environment)))
746 (apply 'start-process "netscape" nil
747 browse-url-netscape-program
748 (append browse-url-netscape-arguments
749 (list "-remote" command)))))
750
751;; --- Mosaic ---
752
753;;;###autoload
754(defun browse-url-mosaic (url &optional new-window)
755 "Ask the XMosaic WWW browser to load URL.
756
757Default to the URL around or before point. The strings in variable
758`browse-url-mosaic-arguments' are also passed to Mosaic and the
759program is invoked according to the variable
760`browse-url-mosaic-program'.
761
762When called interactively, if variable `browse-url-new-window-p' is
763non-nil, load the document in a new Mosaic window, otherwise use a
764random existing one. A non-nil interactive prefix argument reverses
765the effect of `browse-url-new-window-p'.
766
767When called non-interactively, optional second argument NEW-WINDOW is
768used instead of `browse-url-new-window-p'."
769 (interactive (browse-url-interactive-arg "Mosaic URL: "))
770 (let ((pidfile (expand-file-name "~/.mosaicpid"))
771 pid)
772 (if (file-readable-p pidfile)
773 (save-excursion
774 (find-file pidfile)
775 (goto-char (point-min))
776 (setq pid (read (current-buffer)))
777 (kill-buffer nil)))
778 (if (and pid (zerop (signal-process pid 0))) ; Mosaic running
779 (save-excursion
780 (find-file (format "/tmp/Mosaic.%d" pid))
781 (erase-buffer)
01f97560 782 (insert (if (browse-url-maybe-new-window new-window)
8749abea
GM
783 "newwin\n"
784 "goto\n")
785 url "\n")
786 (save-buffer)
787 (kill-buffer nil)
788 ;; Send signal SIGUSR to Mosaic
789 (message "Signalling Mosaic...")
790 (signal-process pid browse-url-usr1-signal)
791 ;; Or you could try:
792 ;; (call-process "kill" nil 0 nil "-USR1" (int-to-string pid))
793 (message "Signalling Mosaic...done")
794 )
795 ;; Mosaic not running - start it
796 (message "Starting Mosaic...")
797 (apply 'start-process "xmosaic" nil browse-url-mosaic-program
798 (append browse-url-mosaic-arguments (list url)))
799 (message "Starting Mosaic...done"))))
800
801;; --- Grail ---
802
803;;;###autoload
804(defvar browse-url-grail
805 (concat (or (getenv "GRAILDIR") "~/.grail") "/user/rcgrail.py")
806 "Location of Grail remote control client script `rcgrail.py'.
807Typically found in $GRAILDIR/rcgrail.py, or ~/.grail/user/rcgrail.py.")
808
809;;;###autoload
810(defun browse-url-grail (url &optional new-window)
811 "Ask the Grail WWW browser to load URL.
812Default to the URL around or before point. Runs the program in the
813variable `browse-url-grail'."
814 (interactive (browse-url-interactive-arg "Grail URL: "))
815 (message "Sending URL to Grail...")
816 (save-excursion
817 (set-buffer (get-buffer-create " *Shell Command Output*"))
818 (erase-buffer)
819 ;; don't worry about this failing.
820 (if new-window
821 (call-process browse-url-grail nil 0 nil "-b" url)
822 (call-process browse-url-grail nil 0 nil url))
823 (message "Sending URL to Grail... done")))
824
825;; --- Mosaic using CCI ---
826
827;;;###autoload
828(defun browse-url-cci (url &optional new-window)
829 "Ask the XMosaic WWW browser to load URL.
830Default to the URL around or before point.
831
832This function only works for XMosaic version 2.5 or later. You must
833select `CCI' from XMosaic's File menu, set the CCI Port Address to the
834value of variable `browse-url-CCI-port', and enable `Accept requests'.
835
836When called interactively, if variable `browse-url-new-window-p' is
837non-nil, load the document in a new browser window, otherwise use a
838random existing one. A non-nil interactive prefix argument reverses
839the effect of `browse-url-new-window-p'.
840
841When called non-interactively, optional second argument NEW-WINDOW is
842used instead of `browse-url-new-window-p'."
843 (interactive (browse-url-interactive-arg "Mosaic URL: "))
844 (open-network-stream "browse-url" " *browse-url*"
845 browse-url-CCI-host browse-url-CCI-port)
846 ;; Todo: start browser if fails
847 (process-send-string "browse-url"
848 (concat "get url (" url ") output "
01f97560 849 (if (browse-url-maybe-new-window new-window)
8749abea
GM
850 "new"
851 "current")
852 "\r\n"))
853 (process-send-string "browse-url" "disconnect\r\n")
854 (delete-process "browse-url"))
855
856;; --- IXI Mosaic ---
857
858;;;###autoload
859(defun browse-url-iximosaic (url &optional new-window)
860 ;; new-window ignored
861 "Ask the IXIMosaic WWW browser to load URL.
862Default to the URL around or before point."
863 (interactive (browse-url-interactive-arg "IXI Mosaic URL: "))
864 (start-process "tellw3b" nil "tellw3b"
865 "-service WWW_BROWSER ixi_showurl " url))
866
867;; --- W3 ---
868
869;;;###autoload
870(defun browse-url-w3 (url &optional new-window)
871 "Ask the w3 WWW browser to load URL.
872Default to the URL around or before point.
873
874When called interactively, if variable `browse-url-new-window-p' is
875non-nil, load the document in a new window. A non-nil interactive
876prefix argument reverses the effect of `browse-url-new-window-p'.
877
878When called non-interactively, optional second argument NEW-WINDOW is
879used instead of `browse-url-new-window-p'."
880 (interactive (browse-url-interactive-arg "W3 URL: "))
881 (require 'w3) ; w3-fetch-other-window not autoloaded
01f97560 882 (if (browse-url-maybe-new-window new-window)
8749abea
GM
883 (w3-fetch-other-window url)
884 (w3-fetch url)))
885
886;;;###autoload
887(defun browse-url-w3-gnudoit (url &optional new-window)
888 ;; new-window ignored
889 "Ask another Emacs running gnuserv to load the URL using the W3 browser.
890The `browse-url-gnudoit-program' program is used with options given by
891`browse-url-gnudoit-args'. Default to the URL around or before point."
892 (interactive (browse-url-interactive-arg "W3 URL: "))
893 (apply 'start-process (concat "gnudoit:" url) nil
894 browse-url-gnudoit-program
01f97560
DL
895 (append browse-url-gnudoit-args
896 (list (concat "(w3-fetch \"" url "\")")
897 "(raise-frame)"))))
8749abea
GM
898
899;; --- Lynx in an xterm ---
900
901;;;###autoload
902(defun browse-url-lynx-xterm (url &optional new-window)
903 ;; new-window ignored
904 "Ask the Lynx WWW browser to load URL.
905Default to the URL around or before point. A new Lynx process is run
906in an Xterm window using the Xterm program named by `browse-url-xterm-program'
907with possible additional arguments `browse-url-xterm-args'."
908 (interactive (browse-url-interactive-arg "Lynx URL: "))
909 (apply #'start-process `(,(concat "lynx" url) nil ,browse-url-xterm-program
01f97560
DL
910 ,@browse-url-xterm-args "-e" "lynx"
911 ,url)))
8749abea
GM
912
913;; --- Lynx in an Emacs "term" window ---
914
915;;;###autoload
916(defun browse-url-lynx-emacs (url &optional new-buffer)
917 "Ask the Lynx WWW browser to load URL.
918Default to the URL around or before point. With a prefix argument, run
919a new Lynx process in a new buffer.
920
921When called interactively, if variable `browse-url-new-window-p' is
922non-nil, load the document in a new lynx in a new term window,
923otherwise use any existing one. A non-nil interactive prefix argument
924reverses the effect of `browse-url-new-window-p'.
925
926When called non-interactively, optional second argument NEW-WINDOW is
927used instead of `browse-url-new-window-p'."
928 (interactive (browse-url-interactive-arg "Lynx URL: "))
929 (let* ((system-uses-terminfo t) ; Lynx uses terminfo
930 ;; (term-term-name "vt100") ; ??
931 (buf (get-buffer "*lynx*"))
932 (proc (and buf (get-buffer-process buf)))
933 (n browse-url-lynx-input-attempts))
01f97560 934 (if (and (browse-url-maybe-new-window new-buffer) buf)
8749abea
GM
935 ;; Rename away the OLD buffer. This isn't very polite, but
936 ;; term insists on working in a buffer named *lynx* and would
937 ;; choke on *lynx*<1>
938 (progn (set-buffer buf)
939 (rename-uniquely)))
01f97560 940 (if (or (browse-url-maybe-new-window new-buffer)
8749abea
GM
941 (not buf)
942 (not proc)
943 (not (memq (process-status proc) '(run stop))))
944 ;; start a new lynx
945 (progn
946 (setq buf
947 (apply #'make-term
01f97560
DL
948 `("lynx" "lynx" nil ,@browse-url-lynx-emacs-args
949 ,url)))
8749abea
GM
950 (switch-to-buffer buf)
951 (term-char-mode)
952 (set-process-sentinel
953 (get-buffer-process buf)
954 ;; Don't leave around a dead one (especially because of its
955 ;; munged keymap.)
956 (lambda (process event)
957 (if (not (memq (process-status process) '(run stop)))
958 (let ((buf (process-buffer process)))
959 (if buf (kill-buffer buf)))))))
960 ;; send the url to lynx in the old buffer
961 (let ((win (get-buffer-window buf t)))
962 (if win
963 (select-window win)
964 (switch-to-buffer buf)))
965 (if (eq (following-char) ?_)
966 (cond ((eq browse-url-lynx-input-field 'warn)
967 (error "Please move out of the input field first."))
968 ((eq browse-url-lynx-input-field 'avoid)
969 (while (and (eq (following-char) ?_) (> n 0))
970 (term-send-down) ; down arrow
971 (sit-for browse-url-lynx-input-delay))
972 (if (eq (following-char) ?_)
973 (error "Cannot move out of the input field, sorry.")))))
974 (term-send-string proc (concat "g" ; goto
975 "\C-u" ; kill default url
976 url
977 "\r")))))
978
979;; --- MMM ---
980
981;;;###autoload
982(defun browse-url-mmm (url &optional new-window)
983 "Ask the MMM WWW browser to load URL.
984Default to the URL around or before point."
985 (interactive (browse-url-interactive-arg "MMM URL: "))
986 (message "Sending URL to MMM...")
987 (save-excursion
988 (set-buffer (get-buffer-create " *Shell Command Output*"))
989 (erase-buffer)
990 ;; mmm_remote just SEGVs if the file isn't there...
991 (if (or (file-exists-p (expand-file-name "~/.mmm_remote"))
992 ;; location in v 0.4:
993 (file-exists-p (expand-file-name "~/.mmm/remote")))
994 (call-process "mmm_remote" nil 0 nil url)
995 (call-process "mmm" nil 0 nil "-external" url))
996 (message "Sending URL to MMM... done")))
997
998;; --- mailto ---
999
1000;;;###autoload
1001(defun browse-url-mail (url &optional new-window)
1002 "Open a new mail message buffer within Emacs.
1003Default to using the mailto: URL around or before point as the
1004recipient's address. Supplying a non-nil interactive prefix argument
1005will cause the mail to be composed in another window rather than the
1006current one.
1007
1008When called interactively, if variable `browse-url-new-window-p' is
1009non-nil use `compose-mail-other-window', otherwise `compose-mail'. A
1010non-nil interactive prefix argument reverses the effect of
1011`browse-url-new-window-p'.
1012
1013When called non-interactively, optional second argument NEW-WINDOW is
1014used instead of `browse-url-new-window-p'."
1015 (interactive (browse-url-interactive-arg "Mailto URL: "))
1016 (save-excursion
1017 (let ((to (if (string-match "^mailto:" url)
1018 (substring url 7)
1019 url)))
01f97560 1020 (if (browse-url-maybe-new-window new-window)
8749abea
GM
1021 (compose-mail-other-window to nil nil nil
1022 (list 'insert-buffer (current-buffer)))
1023 (compose-mail to nil nil nil nil
1024 (list 'insert-buffer (current-buffer)))))))
1025
1026;; --- Random browser ---
1027
1028;;;###autoload
1029(defun browse-url-generic (url &optional new-window)
1030 ;; new-window ignored
1031 "Ask the WWW browser defined by `browse-url-generic-program' to load URL.
1032Default to the URL around or before point. A fresh copy of the
1033browser is started up in a new process with possible additional arguments
1034`browse-url-generic-args'. This is appropriate for browsers which
1035don't offer a form of remote control."
1036 (interactive (browse-url-interactive-arg "URL: "))
1037 (if (not browse-url-generic-program)
1038 (error "No browser defined (`browse-url-generic-program')"))
1039 (apply 'start-process (concat browse-url-generic-program url) nil
1040 browse-url-generic-program
1041 (append browse-url-generic-args (list url))))
1042
1043(provide 'browse-url)
1044
1045;;; browse-url.el ends here