1 ;;; elopher.el --- gopher client
5 ;; Simple gopher client in elisp.
9 (defvar elopher-mode-map
10 (let ((map (make-sparse-keymap)))
11 (define-key map (kbd "<tab>") 'elopher-next-link)
12 (define-key map (kbd "<S-tab>") 'elopher-prev-link)
13 (define-key map (kbd "u") 'elopher-history-back)
14 (when (require 'evil nil t)
15 (evil-define-key 'normal map
16 (kbd "C-]") 'elopher-follow-closest-link
17 (kbd "C-t") 'elopher-history-back
18 (kbd "u") 'elopher-history-back))
20 "Keymap for gopher client.")
22 (define-derived-mode elopher-mode special-mode "elopher"
23 "Major mode for elopher, an elisp gopher client.")
25 (defvar elopher-margin-width 5)
27 (defun elopher-insert-margin (&optional type-name)
30 (format (concat "%" (number-to-string elopher-margin-width) "s")
31 (concat "[" type-name "] "))
32 'face '(foreground-color . "yellow")))
33 (insert (make-string elopher-margin-width ?\s))))
35 (defun elopher-process-record (line)
36 (let* ((type (elt line 0))
37 (fields (split-string (substring line 1) "\t"))
38 (display-string (elt fields 0))
39 (selector (elt fields 1))
40 (hostname (elt fields 2))
42 (address (list selector hostname port)))
44 (?i (elopher-insert-margin)
45 (insert (propertize display-string
46 'face '(foreground-color . "gray"))))
47 (?0 (elopher-insert-margin "T")
48 (insert-text-button display-string
49 'face '(foreground-color . "white")
50 'link-getter #'elopher-get-text
52 'action #'elopher-click-link
54 (?1 (elopher-insert-margin "/")
55 (insert-text-button display-string
56 'face '(foreground-color . "yellow")
57 'link-getter #'elopher-get-index
59 'action #'elopher-click-link
61 (?p (elopher-insert-margin "img")
62 (insert-text-button display-string
63 'face '(foreground-color . "cyan")
64 'link-getter #'elopher-get-image
66 'action #'elopher-click-link
68 (?.) ; Occurs at end of index, can safely ignore.
69 (tp (elopher-insert-margin (concat (char-to-string tp) "?"))
70 (insert (propertize display-string
71 'face '(foreground-color . "red")))))
74 (defvar elopher-incomplete-record "")
76 (defun elopher-process-complete-records (string)
77 (let* ((til-now (string-join (list elopher-incomplete-record string)))
78 (lines (split-string til-now "\r\n")))
79 (dotimes (idx (length lines))
80 (if (< idx (- (length lines) 1))
81 (let ((line (elt lines idx)))
82 (unless (string-empty-p line)
83 (elopher-process-record line)))
84 (setq elopher-incomplete-record (elt lines idx))))))
86 (defun elopher-get-selector (selector host port filter &optional sentinel)
87 (switch-to-buffer "*elopher*")
89 (let ((inhibit-read-only t))
91 (setq elopher-incomplete-record "")
93 :name "elopher-process"
95 :service (if port port 70)
98 (process-send-string "elopher-process" (concat selector "\n")))
100 (defun elopher-index-filter (proc string)
101 (with-current-buffer (get-buffer "*elopher*")
102 (let ((marker (process-mark proc))
103 (inhibit-read-only t))
104 (if (not (marker-position marker))
105 (set-marker marker 0 (current-buffer)))
108 (elopher-process-complete-records string)
109 (set-marker marker (point))))))
111 (defun elopher-get-index (selector host port)
112 (elopher-get-selector selector host port #'elopher-index-filter))
114 (defun elopher-text-filter (proc string)
115 (with-current-buffer (get-buffer "*elopher*")
116 (let ((marker (process-mark proc))
117 (inhibit-read-only t))
118 (if (not (marker-position marker))
119 (set-marker marker 0 (current-buffer)))
122 (dolist (line (split-string string "\r"))
124 (set-marker marker (point))))))
126 (defun elopher-get-text (selector host port)
127 (elopher-get-selector selector host port #'elopher-text-filter))
129 (defvar elopher-image-buffer "")
131 (defun elopher-image-filter (proc string)
132 (setq elopher-image-buffer (concat elopher-image-buffer string)))
134 (defun elopher-image-sentinel (proc event)
135 (let ((inhibit-read-only t))
136 (insert-image (create-image elopher-image-buffer))))
138 (defun elopher-get-image (selector host port)
139 (setq elopher-image-buffer "")
140 (elopher-get-selector selector host port #'elopher-image-filter #'elopher-image-sentinel))
142 (defun elopher-history-back ()
144 (let ((inhibit-read-only t))
147 (defun elopher-next-link ()
151 (defun elopher-prev-link ()
155 (defun elopher-click-link (button)
156 (apply (button-get button 'link-getter) (button-get button 'link-address)))
158 (defun elopher-follow-closest-link ()
163 "Start gopher client."
165 (elopher-get-index "" (read-from-minibuffer "Gopher host: ") 70))
167 ;; (elopher-get-index "" "gopher.floodgap.com" 70)
168 (elopher-get-image "/fun/xkcd/comics/2130/2137/text_entry.png" "gopher.floodgap.com" 70)
170 ;;; elopher.el ends here