Added URL to header.
[elpher.git] / elpher.el
index 244df40..9c0138b 100644 (file)
--- a/elpher.el
+++ b/elpher.el
@@ -4,7 +4,7 @@
 
 ;; Author: Tim Vaughan <tgvaughan@gmail.com>
 ;; Created: 11 April 2019
-;; Version: 2.0.1
+;; Version: 2.2.0
 ;; Keywords: comm gopher
 ;; Homepage: https://github.com/tgvaughan/elpher
 ;; Package-Requires: ((emacs "26"))
@@ -65,7 +65,7 @@
 ;;; Global constants
 ;;
 
-(defconst elpher-version "2.0.1"
+(defconst elpher-version "2.2.0"
   "Current version of elpher.")
 
 (defconst elpher-margin-width 6
@@ -77,7 +77,7 @@
     ((gopher ?4) elpher-get-gopher-node elpher-render-download "bin" elpher-binary)
     ((gopher ?5) elpher-get-gopher-node elpher-render-download "bin" elpher-binary)
     ((gopher ?7) elpher-get-gopher-query-node elpher-render-index "?" elpher-search)
-    ((gopher ?9) elpher-get-gopher-node elpher-render-node-download "bin" elpher-binary)
+    ((gopher ?9) elpher-get-gopher-node elpher-render-download "bin" elpher-binary)
     ((gopher ?g) elpher-get-gopher-node elpher-render-image "img" elpher-image)
     ((gopher ?p) elpher-get-gopher-node elpher-render-image "img" elpher-image)
     ((gopher ?I) elpher-get-gopher-node elpher-render-image "img" elpher-image)
 Otherwise, use the system browser via the BROWSE-URL function."
   :type '(boolean))
 
-(defcustom elpher-buttonify-urls-in-directories nil
+(defcustom elpher-buttonify-urls-in-directories t
   "If non-nil, turns URLs matched in directories into clickable buttons."
   :type '(boolean))
 
@@ -191,13 +191,14 @@ allows switching from an encrypted channel back to plain text without user input
   (let ((data (match-data))) ; Prevent parsing clobbering match data
     (unwind-protect
         (let ((url (url-generic-parse-url url-string)))
-          (setf (url-fullness url) t)
-          (setf (url-filename url)
-                (url-unhex-string (url-filename url)))
-          (unless (url-type url)
-            (setf (url-type url) "gopher"))
-          (when (or (equal "gopher" (url-type url))
-                    (equal "gophers" (url-type url)))
+          (unless (and (not (url-fullness url)) (url-type url))
+            (setf (url-fullness url) t)
+            (setf (url-filename url)
+                  (url-unhex-string (url-filename url)))
+            (unless (url-type url)
+              (setf (url-type url) "gopher"))
+            (when (or (equal "gopher" (url-type url))
+                      (equal "gophers" (url-type url)))
               ;; Gopher defaults
               (unless (url-host url)
                 (setf (url-host url) (url-filename url))
@@ -205,6 +206,10 @@ allows switching from an encrypted channel back to plain text without user input
               (when (or (equal (url-filename url) "")
                         (equal (url-filename url) "/"))
                 (setf (url-filename url) "/1")))
+            (when (equal "gemini" (url-type url))
+              ;; Gemini defaults
+              (if (equal (url-filename url) "")
+                  (setf (url-filename url) "/"))))
           url)
       (set-match-data data))))
 
@@ -213,15 +218,22 @@ allows switching from an encrypted channel back to plain text without user input
 The basic attributes include: TYPE, SELECTOR, HOST and PORT.
 If the optional attribute TLS is non-nil, the address will be marked as
 requiring gopher-over-TLS."
-  (if (and (equal type ?h)
-           (string-prefix-p "URL:" selector))
-      (elpher-address-from-url (elt (split-string selector "URL:") 1))
+  (cond
+   ((and (equal type ?h)
+         (string-prefix-p "URL:" selector))
+    (elpher-address-from-url (elt (split-string selector "URL:") 1)))
+   ((equal type ?8)
+    (elpher-address-from-url
+     (concat "telnet"
+             "://" host
+             ":" (number-to-string port))))
+   (t
     (elpher-address-from-url
      (concat "gopher" (if tls "s" "")
              "://" host
              ":" (number-to-string port)
              "/" (string type)
-             selector))))
+             selector)))))
 
 (defun elpher-make-special-address (type)
   "Create an ADDRESS object corresponding to the given special page symbol TYPE."
@@ -234,7 +246,9 @@ requiring gopher-over-TLS."
     nil))
 
 (defun elpher-address-type (address)
-  "Retrieve selector type from ADDRESS object."
+  "Retrieve type of ADDRESS object.
+This is used to determine how to retrieve and render the document the
+address refers to, via the table `elpher-type-map'."
   (if (symbolp address)
       (list 'special address)
     (let ((protocol (url-type address)))
@@ -246,6 +260,8 @@ requiring gopher-over-TLS."
                      (string-to-char (substring (url-filename address) 1)))))
             ((equal protocol "gemini")
              'gemini)
+            ((equal protocol "telnet")
+             'telnet)
             (t 'other-url)))))
 
 (defun elpher-address-protocol (address)
@@ -267,7 +283,15 @@ For gopher addresses this is a combination of the selector type and selector."
 
 (defun elpher-address-port (address)
   "Retrieve port from ADDRESS object."
-  (url-port address))
+  (if (symbolp address)
+      nil)
+  (if (> (url-port address) 0)
+      (url-port address)
+    (or (and (or (equal (url-type address) "gopher")
+                 (equal (url-type address) "gophers"))
+             70)
+        (and (equal (url-type address) "gemini")
+             1965))))
 
 (defun elpher-address-special-p (address)
   "Return non-nil if ADDRESS object is special (e.g. start page, bookmarks page)."
@@ -400,7 +424,12 @@ unless PRESERVE-PARENT is non-nil."
 (defun elpher-update-header ()
   "If `elpher-use-header' is true, display current node info in window header."
   (if elpher-use-header
-      (setq header-line-format (elpher-node-display-string elpher-current-node))))
+      (let* ((display-string (elpher-node-display-string elpher-current-node))
+             (address (elpher-node-address elpher-current-node))
+             (url-string (if (elpher-address-special-p address)
+                             ""
+                           (concat "  -  " (elpher-address-to-url address) ""))))
+        (setq header-line-format (list display-string url-string)))))
 
 (defmacro elpher-with-clean-buffer (&rest args)
   "Evaluate ARGS with a clean *elpher* buffer as current."
@@ -479,9 +508,7 @@ up to the calling function."
              (proc (open-network-stream "elpher-process"
                                        nil
                                        (elpher-address-host address)
-                                       (if (> (elpher-address-port address) 0)
-                                           (elpher-address-port address)
-                                         70)
+                                       (elpher-address-port address)
                                        :type (if elpher-use-tls 'tls 'plain))))
         (set-process-coding-system proc 'binary)
         (set-process-filter proc
@@ -587,11 +614,11 @@ If ADDRESS is not supplied or nil the record is rendered as an
       (pcase type
         ((or '(gopher ?i) 'nil) ;; Information
          (elpher-insert-margin)
-         (insert (propertize
-                  (if elpher-buttonify-urls-in-directories
-                      (elpher-buttonify-urls display-string)
-                    display-string)
-                  'face 'elpher-info)))
+         (let ((propertized-display-string
+                (propertize display-string 'face 'elpher-info)))
+           (insert (if elpher-buttonify-urls-in-directories
+                       (elpher-buttonify-urls propertized-display-string)
+                     propertized-display-string))))
         (`(gopher ,selector-type) ;; Unknown
          (elpher-insert-margin (concat (char-to-string selector-type) "?"))
          (insert (propertize display-string
@@ -604,7 +631,7 @@ If ADDRESS is not supplied or nil the record is rendered as an
     (elpher-visit-node node)))
 
 (defun elpher-render-index (data &optional _mime-type-string)
-  "Render DATA as an index."
+  "Render DATA as an index.  MIME-TYPE-STRING is unused."
   (elpher-with-clean-buffer
    (if (not data)
        t
@@ -631,11 +658,12 @@ If ADDRESS is not supplied or nil the record is rendered as an
                             'elpher-node  node
                             'action #'elpher-click-link
                             'follow-link t
-                            'help-echo (elpher-node-button-help node))))
+                            'help-echo (elpher-node-button-help node)
+                            'face 'button)))
     (buffer-string)))
 
 (defun elpher-render-text (data &optional _mime-type-string)
-  "Render DATA as text."
+  "Render DATA as text.  MIME-TYPE-STRING is unused."
   (elpher-with-clean-buffer
    (if (not data)
        t
@@ -647,7 +675,7 @@ If ADDRESS is not supplied or nil the record is rendered as an
 ;; Image retrieval
 
 (defun elpher-render-image (data &optional _mime-type-string)
-  "Display DATA as image."
+  "Display DATA as image.  MIME-TYPE-STRING is unused."
   (if (not data)
       nil
     (if (display-images-p)
@@ -696,7 +724,7 @@ The response is rendered using the rendering function RENDERER."
 ;; Raw server response rendering
 
 (defun elpher-render-raw (data &optional _mime-type-string)
-  "Display raw DATA in buffer."
+  "Display raw DATA in buffer.  MIME-TYPE-STRING is unused."
   (if (not data)
       nil
     (elpher-with-clean-buffer
@@ -707,7 +735,7 @@ The response is rendered using the rendering function RENDERER."
 ;; File save "rendering"
 
 (defun elpher-render-download (data &optional _mime-type-string)
-  "Save DATA to file."
+  "Save DATA to file.  MIME-TYPE-STRING is unused."
   (if (not data)
       nil
     (let* ((address (elpher-node-address elpher-current-node))
@@ -727,7 +755,7 @@ The response is rendered using the rendering function RENDERER."
 ;; HTML rendering
 
 (defun elpher-render-html (data &optional _mime-type-string)
-  "Render DATA as HTML using shr."
+  "Render DATA as HTML using shr.  MIME-TYPE-STRING is unused."
   (elpher-with-clean-buffer
    (if (not data)
        t
@@ -750,9 +778,7 @@ The response is stored in the variable ‘elpher-gemini-response’."
            (proc (open-network-stream "elpher-process"
                                       nil
                                       (elpher-address-host address)
-                                      (if (> (elpher-address-port address) 0)
-                                          (elpher-address-port address)
-                                        1965)
+                                      (elpher-address-port address)
                                       :type 'tls)))
       (set-process-coding-system proc 'binary)
       (set-process-filter proc
@@ -883,7 +909,7 @@ The response is assumed to be in the variable `elpher-gemini-response'."
   (let ((address (url-generic-parse-url url)))
     (unless (and (url-type address) (not (url-fullness address))) ;avoid mangling mailto: urls
       (setf (url-fullness address) t)
-      (unless (url-host address) ;if there is an explicit host, filenames are explicit
+      (unless (url-host address) ;if there is an explicit host, filenames are absolute
         (setf (url-host address) (url-host (elpher-node-address elpher-current-node)))
         (unless (string-prefix-p "/" (url-filename address)) ;deal with relative links
           (setf (url-filename address)
@@ -911,7 +937,7 @@ The response is assumed to be in the variable `elpher-gemini-response'."
     (buffer-string))))
 
 (defun elpher-render-gemini-plain-text (data _parameters)
-  "Render DATA as plain text file."
+  "Render DATA as plain text file.  PARAMETERS is currently unused."
   (elpher-with-clean-buffer
    (insert (elpher-buttonify-urls data))
    (elpher-cache-content
@@ -1368,6 +1394,7 @@ If ADDRESS is already bookmarked, update the label only."
     (define-key map (kbd "TAB") 'elpher-next-link)
     (define-key map (kbd "<backtab>") 'elpher-prev-link)
     (define-key map (kbd "u") 'elpher-back)
+    (define-key map [mouse-3] 'elpher-back)
     (define-key map (kbd "O") 'elpher-root-dir)
     (define-key map (kbd "g") 'elpher-go)
     (define-key map (kbd "o") 'elpher-go-current)
@@ -1394,6 +1421,7 @@ If ADDRESS is already bookmarked, update the label only."
         (kbd "C-") 'elpher-follow-current-link
         (kbd "C-t") 'elpher-back
         (kbd "u") 'elpher-back
+        [mouse-3] 'elpher-back
         (kbd "g") 'elpher-go
         (kbd "o") 'elpher-go-current
         (kbd "r") 'elpher-redraw
@@ -1417,7 +1445,7 @@ If ADDRESS is already bookmarked, update the label only."
   "Keymap for gopher client.")
 
 (define-derived-mode elpher-mode special-mode "elpher"
-  "Major mode for elpher, an elisp gopher client.)))))))
+  "Major mode for elpher, an elisp gopher client.
 
 This mode is automatically enabled by the interactive
 functions which initialize the gopher client, namely