;;; ewb.el --- Emacs Web Browser ;; Copyright (C) 2002 Personal Sovereignty Foundation ;; This file is free software; you can redistribute it and/or modify ;; it under the terms of the GNU General Public License as published by ;; the Free Software Foundation; either version 2, or (at your option) ;; any later version. ;; Author: Patrick Anderson (concat (nreverse (string-to-list "gro.scimococe@tunwo"))) ;; Keywords: browser, web, net, XHTML/HTTP/TCP ;;; Installation: ;; Add this to your .emacs ;; (autoload 'ewb "ewb" "emacs web browser" t) ;;; Commentary: ;; For entertainment purposes only ;;; Usage: ;; M-x ewb RET ;; C-h m for keystroke help ;;; Todo: ;; use http-get.el, http-post.el, htmlr.el ;; ;; Text and image data is incrementally (stealthily) downloaded. ;; Download queue is also background managed. ;;; Version: ;; .001 -- Brewing a new mix... ;; Researching htmlr.el, http-get.el, xml.el, nxml, asynch.el ;; 0 -- end of life ;; ewb has overgrown design limitations. ;; The replacement for ewb will parse XHTML directly. ;; -.05 ;; fixed font-lock errors ;; local files should work (use `ewb-local') ;; -.09 -- ;; Much more responsive because i'm using the idea from ;; http://emacswiki.org/cgi-bin/wiki.pl?PrettyLambda to piggy-back ;; onto font-lock. ;; Can now view local files, but cannot follow local links ;; killing stale ewb-connection when entering ewb-get. ;; -.1a -- Fixed dangerous sentinel code (when ewb-connection finally exits, the formatting was being applied to whichever buffer had focus.) ;; -.1 --- Sending a more complete request - sourceforge.net works, but replies with gzipped content. patware.freeshell.org works but ewb-connection hangs. now using HTTP/1.1 ;; -.2a -- Removed referer string. ;; -.2 --- Sending referer. Changed version numbering. ewb-href now accepts . need to move to state-machine parser. ;; -.3 --- Prefix arg or file extensions suppress formatting. write bookmarks to file, customizeable ;; -.4 --- added ewb-revert [(g)], minibuffer state messages, display ewb-url above header, mouse-face, help-echo ;; -.5 --- first alpha. ;;; Customizations: (defgroup ewb nil "Emacs Web Browser" :group 'hypermedia) (defcustom ewb-suppressed-extensions "\.el$\\|\.txt$" "Don't parse these extensions." :group 'ewb) (defcustom ewb-bookmark-file "~/.ewb/bookmarks" "Bookmark file" :group 'ewb) (defcustom ewb-history-file "~/.ewb/history" "History file" :group 'ewb) (defcustom ewb-invisible-markup t "Make markup invisible?" :type 'boolean :group 'ewb) (defcustom ewb-markup-face 'file-name-shadow "Face for markup if `ewb-invisible-markup' is not set." :type 'face :group 'ewb) (defface ewb-note-face '((t (:height 0.5))) "Face for markup if `ewb-invisible-markup' is not set." :group 'ewb) ;; Code: (defun ewb (url &optional prefix) "Emacs Web Browser. \\{ewb-mode-map}" (interactive "sURL: ") (string-match "\\(http://\\)?\\(.*\\)" url) (setq url (concat "http://" (match-string 2 url))) (ewb-parse-url url)) (defun ewb-local (file) "Start `ewb' on a local file." (interactive "fFile: ") (let ((file (file-relative-name file "/"))) (cd (concat "/" (file-name-directory file))) (ewb-parse-url (concat "file://" "/" file)))) (defvar ewb-mode-map (make-sparse-keymap)) (define-key ewb-mode-map [(n)] 'pan-up) (define-key ewb-mode-map [(down)] 'pan-up) (define-key ewb-mode-map [(p)] 'pan-down) (define-key ewb-mode-map [(up)] 'pan-down) (define-key ewb-mode-map [(C)] (lambda (dir) (interactive "DDownload to: ") (cd dir) (start-process "wget" "*wget*" "wget" "-nv" (message (progn (re-search-backward ewb-href) (match-string 1)))))) (define-key ewb-mode-map [(b)] 'ewb-bookmarks) (defun ewb-bookmarks () (interactive) (find-file ewb-bookmark-file)) (defun ewb-bookmark-add () (interactive) (message (concat ewb-url "\nAdded to " ewb-bookmark-file)) (write-region (concat ewb-url "\n") nil ewb-bookmark-file t)) (define-key ewb-mode-map [(B)] 'ewb-bookmark-add) (define-key ewb-mode-map [(o)] 'ewb) (define-key ewb-mode-map [(g)] 'ewb-revert) (defun ewb-revert () (interactive) (ewb ewb-url) (setq ewb-history (cdr ewb-history))) (define-key ewb-mode-map [(9)] 'ewb-next) (defun ewb-next () "jump to next link" (interactive) (re-search-forward ewb-href) (message (match-string 1))) (define-key ewb-mode-map [(shift 9)] 'ewb-prev) (defun ewb-prev () "jump to prev link" (interactive) (re-search-backward ewb-href) (message (match-string 1))) (define-key ewb-mode-map [(13)] 'ewb-follow) (defun ewb-follow () "follow this link" (interactive) (re-search-backward ewb-href) (ewb-parse-url (replace-regexp-in-string "%20" " " (match-string 1)))) (define-key ewb-mode-map [(mouse-2)] 'ewb-follow-mouse) (defun ewb-follow-mouse (event &optional univ) (interactive "e\nP") (goto-char (posn-point (event-end event))) (ewb-follow)) (define-key ewb-mode-map [(s)] 'ewb-search) (defun ewb-search (str) "use google" (interactive "sFor: ") (ewb (concat "www.google.com/search?q=" (dired-replace-in-string " " "+" str)))) (define-key ewb-mode-map [(q)] 'bury-buffer) (define-key ewb-mode-map " " 'scroll-up) (define-key ewb-mode-map [(u)] 'scroll-down) (define-key ewb-mode-map [(h)] (lambda () (interactive) (mapcar 'print ewb-history))) (define-key ewb-mode-map [(meta left)] 'ewb-back) (define-key ewb-mode-map [(backspace)] 'ewb-back) (defun ewb-back () "go back in history" (interactive) (if ewb-history (progn (setq ewb-history (cdr ewb-history)) (let ((cur (car ewb-history))) (setq ewb-history (cdr ewb-history)) ;remove self, since ewb-get adds (ewb cur))))) ;;(defvar ewb-href "]*?\\(?:href\\|name\\)=\"?\\([^\">]+\\)[^>]*?>") ;;(defvar ewb-href "]+\\)\\(.\\|\n\\)*?>") (defvar ewb-href "]+\\)[^>]*>") (defvar ewb-history nil) (defvar ewb-host nil) (defvar ewb-proto nil) (defvar ewb-dir nil) (defvar ewb-url nil) (defvar ewb-connection nil) (defun ewb-mode () "emacs web browser mode. \\{ewb-mode-map}" (interactive) (kill-all-local-variables) ; (setq buffer-read-only t) (use-local-map ewb-mode-map) (setq mode-name "ewb") (setq major-mode 'ewb-mode) (add-to-invisibility-spec 'ewb) (make-local-variable 'font-lock-defaults) (setq font-lock-defaults '(ewb-font-lock-keywords t t nil nil)) ; nil nil ((?_ . "w"))))) (run-hooks 'ewb-mode-hook)) (defconst ewb-font-lock-keywords (eval-when-compile (list `("\\(
\\|

\\|


\\|\\|\\|\\)[^\n]" (1 (progn (backward-char) (insert "\n") ewb-markup-font))) `("\\(.\\|\n\\)*?" (0 (progn 'ewb-note-face))) `("\\(.\\|\n\\)*?\\|\\(.\\|\n\\)*?" (0 'italic)) `("\\(.\\|\n\\)*?" (0 (progn 'bold))) ;; `(".*?" (0 'italic)) ;; `(".*?" (0 'italic)) ;; `(".*?" (0 'italic)) ;; `(".*?" (0 'italic)) `("<\\(.\\|\n\\)*?>\\|&....;" (0 (progn (if ewb-invisible-markup (let ((overlay (make-overlay (match-beginning 0) (match-end 0)))) (overlay-put overlay 'invisible 'ewb) (overlay-put overlay 'intangible 'ewb))) ewb-markup-face))) `("]*>[^Z]" (1 (progn ;; (edebug-br) (backward-char) (insert "Z") (if (= 0 (length ewb-host)) ;;if we're browsing locally (insert-image (create-image (match-string 1)))) ;; (if ewb-invisible-markup ;; (insert (match-string 1))) ewb-markup-font))) `(,(concat ewb-href "\\(\\(.\\|\n\\)*?\\)
") (4 (progn (let (( overlay (make-overlay (match-beginning 4) (match-end 4)))) (overlay-put overlay 'mouse-face 'highlight) (overlay-put overlay 'help-echo (match-string 1))) font-lock-keyword-face))) `(" [^ ]" (0 (progn (backward-char) (insert " ") font-lock-string-face))) `(""[^\"]" (0 (progn (backward-char) (insert "\"") font-lock-string-face))) (cons (regexp-opt '("HTTP/1.1" "Date:" "Server:" "Keep-Alive:" "Connection:" "Transfer-Encoding:" "Content-Type:") 'words) 'font-lock-builtin-face) ))) (defun ewb-parse-url (url) "An optional PROTO looks like http: or file: An optional HOST looks like //(a.b.c)? An optional DIR looks like /?a.b/c.d/ An optional FILE looks like a.b" ;; 1=proto:?, 2=//host? 3=/dir/* 5=file? (string-match "\\(http:\\|file:\\)?\\(//[^/\n]*\\)?\\(\\([^/\n]*/\\)*\\)\\(.*\\)" url) (let* ((proto (match-string 1 url)) (host (match-string 2 url)) (dir (match-string 3 url)) (file (match-string 5 url)) (buf (get-buffer-create "*Emacs Web Browser*"))) (if (> (length proto) 0) (setq ewb-proto proto)) (if (> (length host) 0) (progn (string-match "\\(//\\)?\\(.*\\)" host) (setq host (match-string 2 host)) ;;strip leading // if present (setq ewb-host host))) ;;this will be nil for file: (if (> (length dir) 0) ;;if dir is full, (setq ewb-dir dir) ;;update cwd (if (= 0 (length file)) ;;else, if file is empty (setq ewb-dir "/"))) ;;make dir root ;; (if dir ;; (if root ;; (setq ewb-dir (concat "/" dir)) ;; (progn ;; ;; (setq dir (file-relative-name dir ewb-dir)) ;; (setq ewb-dir (concat ewb-dir dir)))) ;; (if (not file) ;; (setq ewb-dir "/" ))) ;; (cd ewb-dir) ;; (setq ewb-dir (file-relative-name ewb-dir "/")) (setq ewb-url (concat ewb-proto "//" ewb-host ewb-dir file)) (if (file-exists-p ewb-history-file) (write-region (concat ewb-url "\n") nil ewb-history-file t)) (setq ewb-history (cons ewb-url ewb-history)) (switch-to-buffer buf) (erase-buffer) (insert (concat ewb-url "\n")) (overlay-put (make-overlay 1 (+ (length ewb-url) 1)) 'face 'font-lock-constant-face) (if (> (length ewb-host) 0) (ewb-get ewb-host ewb-dir file buf) (progn ;; (cd ewb-dir) (insert-file-contents (concat ewb-dir file)))) (ewb-mode))) (defun ewb-get (host dir file buf) "get over http" (message (concat "downloading " ewb-url "...")) (if ewb-connection (progn (delete-process ewb-connection) (setq ewb-connection nil))) (setq ewb-connection (open-network-stream "ewb-connection" buf host 80)) (process-send-string ewb-connection (concat "GET " (replace-regexp-in-string " " "%20" (concat dir file)) " HTTP/1.1\r\n" "Host: " host "\r\n" "User-Agent: Emacs Web Browser\r\n" "Accept: */*\r\n" "Accept-Language: *\r\n" "Accept-Encoding: gzip, deflate, compress\r\n" "Accept-Charset: ISO-8859-1, utf-8\r\n" "Keep-Alive: 300\r\n" "Connection: keep-alive\r\n" "\r\n"))) (provide 'ewb) ;;; ewb.el ends here