+(defun elpher-get-telnet-node ()
+ "Getter which opens a telnet connection to the server specified by the current node."
+ (let* ((address (elpher-node-address elpher-current-node))
+ (host (elpher-address-host address))
+ (port (elpher-address-port address)))
+ (elpher-visit-parent-node)
+ (telnet host port)))
+
+;; Start page node retrieval
+
+(defun elpher-get-start-node ()
+ "Getter which displays the start page."
+ (elpher-with-clean-buffer
+ (insert " --------------------------------------------\n"
+ " Elpher Gopher Client \n"
+ " version " elpher-version "\n"
+ " --------------------------------------------\n"
+ "\n"
+ "Default bindings:\n"
+ "\n"
+ " - TAB/Shift-TAB: next/prev item on current page\n"
+ " - RET/mouse-1: open item under cursor\n"
+ " - m: select an item on current page by name (autocompletes)\n"
+ " - u: return to previous page\n"
+ " - o/O: visit different selector or the root menu of the current server\n"
+ " - g: go to a particular gopher address\n"
+ " - i/I: info on item under cursor or current page\n"
+ " - c/C: copy URL representation of item under cursor or current page\n"
+ " - a/A: bookmark the item under cursor or current page\n"
+ " - x/X: remove bookmark for item under cursor or current page\n"
+ " - B: visit the bookmarks page\n"
+ " - r: redraw current page (using cached contents if available)\n"
+ " - R: reload current page (regenerates cache)\n"
+ " - T: toggle TLS mode\n"
+ " - d/D: download item under cursor or current page\n"
+ " - .: display the raw server response for the current page\n"
+ " - S: set an explicit character coding system (default is to autodetect)\n"
+ "\n"
+ "Start your exploration of gopher space:\n")
+ (elpher-insert-index-record "Floodgap Systems Gopher Server"
+ (elpher-make-address ?1 "" "gopher.floodgap.com" 70))
+ (insert "\n"
+ "Alternatively, select the following item and enter some search terms:\n")
+ (elpher-insert-index-record "Veronica-2 Gopher Search Engine"
+ (elpher-make-address ?7 "/v2/vs" "gopher.floodgap.com" 70))
+ (insert "\n"
+ "** Refer to the ")
+ (let ((help-string "RET,mouse-1: Open Elpher info manual (if available)"))
+ (insert-text-button "Elpher info manual"
+ 'face 'link
+ 'action (lambda (button)
+ (interactive)
+ (info "(elpher)"))
+ 'follow-link t
+ 'help-echo help-string))
+ (insert " for the full documentation. **\n")
+ (insert (propertize
+ (concat " (This should be available if you have installed Elpher using\n"
+ " MELPA. Otherwise you will have to install the manual yourself.)")
+ 'face 'shadow))
+ (elpher-restore-pos)))
+
+;; Bookmarks page node retrieval
+
+(defun elpher-get-bookmarks-node ()
+ "Getter to load and display the current bookmark list."
+ (elpher-with-clean-buffer
+ (insert "---- Bookmark list ----\n\n")
+ (let ((bookmarks (elpher-load-bookmarks)))
+ (if bookmarks
+ (dolist (bookmark bookmarks)
+ (let ((display-string (elpher-bookmark-display-string bookmark))
+ (address (elpher-bookmark-address bookmark)))
+ (elpher-insert-index-record display-string address)))
+ (insert "No bookmarks found.\n")))
+ (insert "\n-----------------------\n\n"
+ "- u: return to previous page\n"
+ "- x: delete selected bookmark\n"
+ "- a: rename selected bookmark\n\n"
+ "Bookmarks are stored in the file "
+ (locate-user-emacs-file "elpher-bookmarks"))
+ (elpher-restore-pos)))
+
+
+;;; Bookmarks
+;;
+
+(defun elpher-make-bookmark (display-string address)
+ "Make an elpher bookmark.
+DISPLAY-STRING determines how the bookmark will appear in the
+bookmark list, while ADDRESS is the address of the entry."
+ (list display-string address))
+
+(defun elpher-bookmark-display-string (bookmark)
+ "Get the display string of BOOKMARK."
+ (elt bookmark 0))
+
+(defun elpher-set-bookmark-display-string (bookmark display-string)
+ "Set the display string of BOOKMARK to DISPLAY-STRING."
+ (setcar bookmark display-string))
+
+(defun elpher-bookmark-address (bookmark)
+ "Get the address for BOOKMARK."
+ (elt bookmark 1))
+
+(defun elpher-save-bookmarks (bookmarks)
+ "Record the bookmark list BOOKMARKS to the user's bookmark file.
+Beware that this completely replaces the existing contents of the file."
+ (with-temp-file (locate-user-emacs-file "elpher-bookmarks")
+ (erase-buffer)
+ (insert "; Elpher gopher bookmarks file\n\n"
+ "; Bookmarks are stored as a list of (label (type selector host port))\n"
+ "; s-expressions, where type is stored as a character (i.e. 49 = ?1).\n"
+ "; Feel free to edit by hand, but ensure this structure remains intact.\n\n")
+ (pp bookmarks (current-buffer))))
+
+(defun elpher-load-bookmarks ()
+ "Get the list of bookmarks from the users's bookmark file."
+ (with-temp-buffer
+ (ignore-errors
+ (insert-file-contents (locate-user-emacs-file "elpher-bookmarks"))
+ (goto-char (point-min))
+ (read (current-buffer)))))
+
+(defun elpher-add-address-bookmark (address display-string)
+ "Save a bookmark for ADDRESS with label DISPLAY-STRING.
+If ADDRESS is already bookmarked, update the label only."
+ (let ((bookmarks (elpher-load-bookmarks)))
+ (let ((existing-bookmark (rassoc (list address) bookmarks)))
+ (if existing-bookmark
+ (elpher-set-bookmark-display-string existing-bookmark display-string)
+ (add-to-list 'bookmarks (elpher-make-bookmark display-string address))))
+ (elpher-save-bookmarks bookmarks)))
+
+(defun elpher-remove-address-bookmark (address)
+ "Remove any bookmark to ADDRESS."
+ (elpher-save-bookmarks
+ (seq-filter (lambda (bookmark)
+ (not (equal (elpher-bookmark-address bookmark) address)))
+ (elpher-load-bookmarks))))
+
+
+;;; Interactive procedures