Version number bump.
[elpher.git] / elpher.el
index 9e1471b..56d82c4 100644 (file)
--- a/elpher.el
+++ b/elpher.el
@@ -4,7 +4,7 @@
 
 ;; Author: Tim Vaughan <tgvaughan@gmail.com>
 ;; Created: 11 April 2019
-;; Version: 1.2.3
+;; Version: 1.3.0
 ;; Keywords: comm gopher
 ;; Homepage: https://github.com/tgvaughan/elpher
 ;; Package-Requires: ((emacs "25"))
@@ -57,7 +57,7 @@
 ;;; Global constants
 ;;
 
-(defconst elpher-version "1.2.3"
+(defconst elpher-version "1.3.0"
   "Current version of elpher.")
 
 (defconst elpher-margin-width 6
@@ -79,7 +79,7 @@
          "i - m: select an item on current page by name (autocompletes)\tfake\tfake\t1"
          "i - u: return to parent\tfake\tfake\t1"
          "i - O: visit the root menu of the current server\tfake\tfake\t1"
-         "i - g: go to a particular menu or item\tfake\tfake\t1"
+         "i - g: go to a particular gopher address\tfake\tfake\t1"
          "i - i/I: info on item under cursor or current page\tfake\tfake\t1"
          "i - c/C: copy URL representation of item under cursor or current page\tfake\tfake\t1"
          "i - a/A: bookmark the item under cursor or current page\tfake\tfake\t1"
@@ -89,6 +89,7 @@
          "i - R: reload current page (regenerates cache)\tfake\tfake\t1"
          "i - d: download directory entry under cursor\tfake\tfake\t1"
          "i - w: display the raw server response for the current page\tfake\tfake\t1"
+         "i - S: set an explicit character coding system (default is to autodetect)\tfake\tfake\t1"
          "i\tfake\tfake\t1"
          "iWhere to start exploring Gopherspace:\tfake\tfake\t1"
          "i\tfake\tfake\t1"
   "Source for elpher start page.")
 
 (defconst elpher-type-map
-  '((?0 elpher-get-text-node "T" elpher-text)
+  '((?0 elpher-get-text-node "txt" elpher-text)
     (?1 elpher-get-index-node "/" elpher-index)
-    (?4 elpher-get-node-download "B" elpher-binary)
-    (?5 elpher-get-node-download "B" elpher-binary)
+    (?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 "?" elpher-telnet)
-    (?9 elpher-get-node-download "B" elpher-binary)
-    (?g elpher-get-image-node "im" elpher-image)
-    (?p elpher-get-image-node "im" elpher-image)
-    (?I elpher-get-image-node "im" elpher-image)
-    (?d elpher-get-node-download "d" elpher-binary)
-    (?h elpher-get-url-node "W" elpher-url)
+    (?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 "web" elpher-url)
     (bookmarks elpher-get-bookmarks-node "#" elpher-index)
     (start elpher-get-start-node "#" elpher-index))
   "Association list from types to getters, margin codes and index faces.")
@@ -340,14 +341,28 @@ unless PRESERVE-PARENT is non-nil."
                 args)))
 
 
-;;; Index rendering
+;;; Text Processing
 ;;
 
+(defvar elpher-user-coding-system nil
+  "User-specified coding system to use for decoding text responses.")
+
+(defun elpher-decode (string)
+  "Decode STRING using autodetected or user-specified coding system."
+  (decode-coding-string string
+                        (if elpher-user-coding-system
+                            elpher-user-coding-system
+                          (detect-coding-string string t))))
+
 (defun elpher-preprocess-text-response (string)
-  "Clear away CRs and terminating period from STRING."
-  (replace-regexp-in-string "\n\.\n$" "\n"
-                            (replace-regexp-in-string "\r" ""
-                                                      string)))
+  "Preprocess text selector response contained in STRING.
+This involes decoding the character representation, and clearing
+away CRs and any terminating period."
+  (elpher-decode (replace-regexp-in-string "\n\.\n$" "\n"
+                                           (replace-regexp-in-string "\r" "" string))))
+
+;;; Index rendering
+;;
 
 (defun elpher-insert-index (string)
   "Insert the index corresponding to STRING into the current buffer."
@@ -389,6 +404,7 @@ unless PRESERVE-PARENT is non-nil."
               (elpher-address-host address)
               (elpher-address-port address)))))
 
+
 (defun elpher-insert-index-record (display-string type selector host port)
   "Function to insert an index record into the current buffer.
 The contents of the record are dictated by TYPE, DISPLAY-STRING, SELECTOR, HOST
@@ -445,6 +461,8 @@ The result is stored as a string in the variable ‘elpher-selector-string’."
         (make-network-process :name "elpher-process"
                               :host (elpher-address-host address)
                               :service (elpher-address-port address)
+                              :coding 'no-conversion
+                              :filter-multibyte nil
                               :filter (lambda (proc string)
                                         (setq elpher-selector-string
                                               (concat elpher-selector-string string)))
@@ -575,9 +593,7 @@ calls, as is necessary if the match is performed by `string-match'."
                                  (lambda (proc event)
                                    (unless (string-prefix-p "deleted" event)
                                      (let ((image (create-image
-                                                   (encode-coding-string
-                                                    elpher-selector-string
-                                                    'no-conversion)
+                                                   elpher-selector-string
                                                    nil t)))
                                        (elpher-with-clean-buffer
                                         (insert-image image)
@@ -941,12 +957,14 @@ host, selector and port."
 (defun elpher-bookmark-current ()
   "Bookmark the current node."
   (interactive)
-  (unless (elpher-bookmarks-current-p)
-      (let ((address (elpher-node-address elpher-current-node))
-            (display-string (read-string "Bookmark display string: "
-                                         (elpher-node-display-string elpher-current-node))))
-        (elpher-add-address-bookmark address display-string)
-        (message "Bookmark added."))))
+  (let ((address (elpher-node-address elpher-current-node))
+        (display-string (elpher-node-display-string elpher-current-node)))
+    (if (not (elpher-address-special-p address))
+        (let ((bookmark-display-string (read-string "Bookmark display string: "
+                                                    display-string)))
+          (elpher-add-address-bookmark address bookmark-display-string)
+          (message "Bookmark added."))
+      (error "Cannot bookmark %s" display-string))))
 
 (defun elpher-bookmark-link ()
   "Bookmark the link at point."
@@ -955,19 +973,23 @@ host, selector and port."
     (if button
         (let* ((node (button-get button 'elpher-node))
                (address (elpher-node-address node))
-               (display-string (read-string "Bookmark display string: "
-                                            (elpher-node-display-string node))))
-          (elpher-add-address-bookmark address display-string)
-          (elpher-reload-bookmarks)
-          (message "Bookmark added."))
+               (display-string (elpher-node-display-string node)))
+          (if (not (elpher-address-special-p address))
+              (let ((bookmark-display-string (read-string "Bookmark display string: "
+                                                          display-string)))
+                (elpher-add-address-bookmark address bookmark-display-string)
+                (elpher-reload-bookmarks)
+                (message "Bookmark added."))
+            (error "Cannot bookmark %s" display-string)))
       (error "No link selected"))))
 
 (defun elpher-unbookmark-current ()
   "Remove bookmark for the current node."
   (interactive)
-  (unless (elpher-bookmarks-current-p)
-    (elpher-remove-address-bookmark (elpher-node-address elpher-current-node))
-    (message "Bookmark removed.")))
+  (let ((address (elpher-node-address elpher-current-node)))
+    (unless (elpher-address-special-p address)
+      (elpher-remove-address-bookmark address)
+      (message "Bookmark removed."))))
 
 (defun elpher-unbookmark-link ()
   "Remove bookmark for the link at point."
@@ -985,13 +1007,13 @@ host, selector and port."
   (interactive)
   (switch-to-buffer "*elpher*")
   (elpher-visit-node
-   (elpher-make-node "Bookmarks" (elpher-make-address 'bookmarks))))
+   (elpher-make-node "Bookmarks Page" (elpher-make-address 'bookmarks))))
 
 (defun elpher-info-node (node)
   "Display information on NODE."
   (let ((display-string (elpher-node-display-string node))
         (address (elpher-node-address node)))
-    (if address
+    (if (not (elpher-address-special-p address))
         (message "`%s' on %s port %s"
                 (elpher-address-selector address)
                 (elpher-address-host address)
@@ -1013,23 +1035,29 @@ host, selector and port."
 
 (defun elpher-get-address-url (address)
   "Get URL representation of ADDRESS."
-  (concat "gopher://"
-          (elpher-address-host address)
-          (let ((port (elpher-address-port address)))
-            (if (equal port 70)
-                ""
-              (format ":%d" port)))
-          "/" (string (elpher-address-type address))
-          (elpher-address-selector 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://"
+              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)))
-    (if address
-        (let ((url (elpher-get-address-url address)))
-          (message url)
-          (kill-new url))
-      (error (format "Cannot represent %s as URL" (elpher-node-display-string node))))))
+    (if (elpher-address-special-p address)
+        (error (format "Cannot represent %s as URL" (elpher-node-display-string node)))
+      (let ((url (elpher-get-address-url address)))
+        (message "Copied \"%s\" to kill-ring/clipboard." url)
+        (kill-new url)))))
 
 (defun elpher-copy-link-url ()
   "Copy URL of item at point to `kill-ring'."
@@ -1044,6 +1072,15 @@ host, selector and port."
   (interactive)
   (elpher-copy-node-url elpher-current-node))
 
+(defun elpher-set-coding-system ()
+  "Specify an explicit character coding system."
+  (interactive)
+  (let ((system (read-coding-system "Set coding system to use (default is to autodetect): " nil)))
+    (setq elpher-user-coding-system system)
+    (if system
+        (message "Coding system fixed to %s. (Reload to see effect)." system)
+      (message "Coding system set to autodetect. (Reload to see effect)."))))
+
 ;;; Mode and keymap
 ;;
 
@@ -1068,6 +1105,7 @@ host, selector and port."
     (define-key map (kbd "x") 'elpher-unbookmark-link)
     (define-key map (kbd "X") 'elpher-unbookmark-current)
     (define-key map (kbd "B") 'elpher-bookmarks)
+    (define-key map (kbd "S") 'elpher-set-coding-system)
     (when (fboundp 'evil-define-key)
       (evil-define-key 'motion map
         (kbd "TAB") 'elpher-next-link
@@ -1089,7 +1127,8 @@ host, selector and port."
         (kbd "A") 'elpher-bookmark-current
         (kbd "x") 'elpher-unbookmark-link
         (kbd "X") 'elpher-unbookmark-current
-        (kbd "B") 'elpher-bookmarks))
+        (kbd "B") 'elpher-bookmarks
+        (kbd "S") 'elpher-set-coding-system))
     map)
   "Keymap for gopher client.")