X-Git-Url: https://thelambdalab.xyz/gitweb/index.cgi?a=blobdiff_plain;f=elpher.el;h=14e2db486f364a9b9aac120167631d8332a4568b;hb=c80e07878361f6e3a7646ca40900c4da98c7bf5c;hp=2afe674f4b561ffdc543c4be2a6f635bfd4609d4;hpb=fa1d9bd99e08b50e0aed3b42e5f8d4bf64039b8e;p=elpher.git diff --git a/elpher.el b/elpher.el index 2afe674..14e2db4 100644 --- a/elpher.el +++ b/elpher.el @@ -4,10 +4,10 @@ ;; Author: Tim Vaughan ;; Created: 11 April 2019 -;; Version: 1.4.5 +;; Version: 1.4.6 ;; Keywords: comm gopher ;; Homepage: https://github.com/tgvaughan/elpher -;; Package-Requires: ((emacs "25")) +;; Package-Requires: ((emacs "26")) ;; This file is not part of GNU Emacs. @@ -50,37 +50,52 @@ ;;; Code: (provide 'elpher) + +;;; Dependencies +;; + (require 'seq) (require 'pp) (require 'shr) +(require 'url-util) + ;;; Global constants ;; -(defconst elpher-version "1.4.5" +(defconst elpher-version "1.4.6" "Current version of elpher.") (defconst elpher-margin-width 6 "Width of left-hand margin used when rendering indicies.") - -(defconst elpher-type-map - '((?0 elpher-get-text-node "txt" elpher-text) - (?1 elpher-get-index-node "/" elpher-index) - (?4 elpher-get-node-download "bin" elpher-binary) - (?5 elpher-get-node-download "bin" elpher-binary) - (?7 elpher-get-search-node "?" elpher-search) - (?8 elpher-get-telnet-node "tel" elpher-telnet) - (?9 elpher-get-node-download "bin" elpher-binary) - (?g elpher-get-image-node "img" elpher-image) - (?p elpher-get-image-node "img" elpher-image) - (?I elpher-get-image-node "img" elpher-image) - (?d elpher-get-node-download "doc" elpher-binary) - (?h elpher-get-url-node "url" elpher-url) - (bookmarks elpher-get-bookmarks-node "#" elpher-index) - (start elpher-get-start-node "#" elpher-index)) +(defconst elpher-transport-map + '(("gopher" elpher-get-gopher-selector) + ("gophers" elpher-get-gopher-selector) + ("gemini" elpher-get-gemini-selector))) + +(defconst elpher-gopher-type-map + '((?0 elpher-display-text "txt" elpher-text) + (?1 elpher-display-gophermap "/" elpher-index) + (?4 nil "bin" elpher-binary) + (?5 nil "bin" elpher-binary) + (?7 elpher-display-search-node "?" elpher-search) + (?8 elpher-display-telnet-node "tel" elpher-telnet) + (?9 nil "bin" elpher-binary) + (?g elpher-display-image-node "img" elpher-image) + (?p elpher-display-image-node "img" elpher-image) + (?I elpher-display-image-node "img" elpher-image) + (?d nil "doc" elpher-binary) + (?P nil "doc" elpher-binary) + (?s nil "snd" elpher-binary) + (?h elpher-display-node-html "htm" elpher-html)) "Association list from types to getters, margin codes and index faces.") +(defconst elpher-mime-type-map + '(("text/gemini" elpher-display-node-text) + ("text/html" elpher-display-node-html) + ("text/*" elpher-display-node-text) + ("image/*" elpher-display-image-node))) ;;; Customization group ;; @@ -146,10 +161,6 @@ Otherwise, use the system browser via the BROWSE-URL function." "If non-nil, turns URLs matched in directories into clickable buttons." :type '(boolean)) -(defcustom elpher-cache-images nil - "If non-nil, cache images in memory in the same way as other content." - :type '(boolean)) - (defcustom elpher-use-header t "If non-nil, display current node information in buffer header." :type '(boolean)) @@ -166,40 +177,53 @@ allows switching from an encrypted channel back to plain text without user input ;; Address -(defun elpher-make-address (type &optional selector host port use-tls) - "Create an address of a gopher object with TYPE, SELECTOR, HOST and PORT. -Although selector host and port are optional, they are only omitted for -special address types, such as 'start for the start page. +(defun elpher-make-address-from-url (url) + "Create an elpher address corresponding to the given URL." + (let ((url (url-generic-parse-url url-string))) + (if (and (url-type url) + (url-host url)) + (setf (url-filename url) (url-unhex-string (url-filename url))) + (error "Malformed URL" url)))) + +(defun elpher-address-get-url (address) + "Get URL representation of ADDRESS." + (url-encode-url (url-recreate address))) -Setting the USE-TLS parameter to non-nil causes Elpher to engage TLS mode -before attempting to connect to the server." - (if use-tls - (list type selector host port 'tls) - (list type selector host port))) +(defun elpher-address-gopher-p? (address) + "Return non-nil if ADDRESS specifies a gopher address." + (let ((protocol (url-type address))) + (if (or (string-equal protocol "gopher") + (string-equal protocol "gophers"))))) (defun elpher-address-type (address) - "Retrieve type from ADDRESS." - (elt address 0)) + "Retrieve selector type from ADDRESS." + (let ((filename (url-filename address))) + (if (> (length filename) 0) + (string-to-char filename) + ?1))) (defun elpher-address-selector (address) "Retrieve selector from ADDRESS." - (elt address 1)) + (let ((filename (url-filename address))) + (if (> (length filename) 0) + (substring filename 1) + ""))) (defun elpher-address-host (address) "Retrieve host from ADDRESS." - (elt address 2)) + (url-host address)) (defun elpher-address-port (address) "Retrieve port from ADDRESS." - (elt address 3)) + (url-port address)) (defun elpher-address-use-tls-p (address) "Return non-nil if ADDRESS is marked as needing TLS." - (elt address 4)) + (string-equal (url-type address) "gophers")) (defun elpher-address-special-p (address) "Return non-nil if ADDRESS is special (e.g. start page, bookmarks page)." - (not (elpher-address-host address))) + (symbolp address)) ;; Node @@ -507,7 +531,7 @@ up to the calling function." ;; Text retrieval (defconst elpher-url-regex - "\\([a-zA-Z]+\\)://\\([a-zA-Z0-9.\-]+\\)\\(?3::[0-9]+\\)?\\(?4:/[^ \r\n\t(),]*\\)?" + "\\([a-zA-Z]+\\)://\\([a-zA-Z0-9.\-]+\\|\[[a-zA-Z0-9:]+\]\\)\\(?3::[0-9]+\\)?\\(?4:/[^ \r\n\t(),]*\\)?" "Regexp used to locate and buttinofy URLs in text files loaded by elpher.") (defun elpher-make-node-from-matched-url (&optional string) @@ -519,7 +543,10 @@ calls, as is necessary if the match is performed by `string-match'." (protocol (downcase (match-string 1 string)))) (if (or (string= protocol "gopher") (string= protocol "gophers")) - (let* ((host (match-string 2 string)) + (let* ((bare-host (match-string 2 string)) + (host (if (string-prefix-p "[" bare-host) + (substring bare-host 1 (- (length bare-host) 1)) + bare-host)) (port (if (> (length (match-string 3 string)) 1) (string-to-number (substring (match-string 3 string) 1)) 70)) @@ -527,9 +554,11 @@ calls, as is necessary if the match is performed by `string-match'." (type (if (> (length type-and-selector) 1) (elt type-and-selector 1) ?1)) - (selector (if (> (length type-and-selector) 1) - (substring type-and-selector 2) - "")) + (selector (decode-coding-string + (url-unhex-string + (if (> (length type-and-selector) 1) + (substring type-and-selector 2) + "")) 'utf-8)) (use-tls (string= protocol "gophers")) (address (elpher-make-address type selector host port use-tls))) (elpher-make-node url address)) @@ -585,31 +614,21 @@ calls, as is necessary if the match is performed by `string-match'." (defun elpher-get-image-node () "Getter which retrieves the current node contents as an image to view." - (let* ((address (elpher-node-address elpher-current-node)) - (content (elpher-get-cached-content address))) - (if content + (let* ((address (elpher-node-address elpher-current-node))) + (if (display-images-p) (progn (elpher-with-clean-buffer - (insert-image content) - (elpher-restore-pos))) - (if (display-images-p) - (progn - (elpher-with-clean-buffer - (insert "LOADING IMAGE... (use 'u' to cancel)")) - (elpher-get-selector address - (lambda (proc event) - (unless (string-prefix-p "deleted" event) - (let ((image (create-image - elpher-selector-string - nil t))) - (elpher-with-clean-buffer - (insert-image image) - (elpher-restore-pos)) - (if elpher-cache-images - (elpher-cache-content - (elpher-node-address elpher-current-node) - image))))))) - (elpher-get-node-download))))) + (insert "LOADING IMAGE... (use 'u' to cancel)")) + (elpher-get-selector address + (lambda (proc event) + (unless (string-prefix-p "deleted" event) + (let ((image (create-image + elpher-selector-string + nil t))) + (elpher-with-clean-buffer + (insert-image image) + (elpher-restore-pos))))))) + (elpher-get-node-download)))) ;; Search retrieval @@ -653,17 +672,12 @@ calls, as is necessary if the match is performed by `string-match'." (let ((address (elpher-node-address elpher-current-node))) (elpher-with-clean-buffer (insert "LOADING RAW SERVER RESPONSE... (use 'u' to cancel)")) - (if address - (elpher-get-selector address - (lambda (proc event) - (unless (string-prefix-p "deleted" event) - (elpher-with-clean-buffer - (insert elpher-selector-string) - (goto-char (point-min)))))) - (progn - (elpher-with-clean-buffer - (insert elpher-start-index)) - (goto-char (point-min))))) + (elpher-get-selector address + (lambda (proc event) + (unless (string-prefix-p "deleted" event) + (elpher-with-clean-buffer + (insert elpher-selector-string) + (goto-char (point-min))))))) (message "Displaying raw server response. Reload or redraw to return to standard view.")) ;; File export retrieval @@ -877,6 +891,7 @@ If ADDRESS is already bookmarked, update the label only." (not (equal (elpher-bookmark-address bookmark) address))) (elpher-load-bookmarks)))) + ;;; Interactive procedures ;; @@ -1122,25 +1137,6 @@ host, selector and port." (interactive) (elpher-info-node elpher-current-node)) -(defun elpher-get-address-url (address) - "Get URL representation of ADDRESS." - (let ((type (elpher-address-type address)) - (selector (elpher-address-selector address)) - (host (elpher-address-host address)) - (port (elpher-address-port address))) - (if (and (equal type ?h) - (string-prefix-p "URL:" selector)) - (elt (split-string selector "URL:") 1) - (concat "gopher" - (if (elpher-address-use-tls-p address) "s" "") - "://" - host - (if (equal port 70) - "" - (format ":%d" port)) - "/" (string type) - selector)))) - (defun elpher-copy-node-url (node) "Copy URL representation of address of NODE to `kill-ring'." (let ((address (elpher-node-address node))) @@ -1172,6 +1168,7 @@ host, selector and port." (message "Coding system fixed to %s. (Reload to see effect)." system) (message "Coding system set to autodetect. (Reload to see effect).")))) + ;;; Mode and keymap ;; @@ -1239,6 +1236,7 @@ functions which initialize the gopher client, namely (when (fboundp 'evil-set-initial-state) (evil-set-initial-state 'elpher-mode 'motion)) + ;;; Main start procedure ;;