Added text links.
[elpher.git] / elopher.el
1 ;;; elopher.el --- gopher client
2
3 ;;; Commentary:
4
5 ;; Simple gopher client in elisp.
6
7 ;;; Code:
8
9 ;; (defvar elopher-mode-map nil "Keymap for gopher client.")
10 ;; (define-key elopher-mode-map (kbd "p") 'elopher-quit)
11
12 ;; (define-derived-mode elopher-mode special-mode "elopher"
13 ;;   "Major mode for elopher, an elisp gopher client.")
14
15 ;; (global-set-key (kbd "C-c C-b") 'eval-buffer)
16
17 (defvar elopher-type-margin-width 5)
18
19 (defvar elopher-history '())
20
21 (defun elopher-type-margin (&optional type-name)
22   (if type-name
23       (insert (propertize
24                (format (concat "%" (number-to-string elopher-type-margin-width) "s")
25                        (concat "[" type-name "] "))
26                'face '(foreground-color . "yellow")))
27     (insert (make-string elopher-type-margin-width ?\s))))
28
29 (defun elopher-make-clickable (string link-function mouse-help)
30   (let ((map (make-sparse-keymap)))
31     (define-key map [mouse-1] link-function)
32     (define-key map (kbd "RET") link-function)
33     (propertize string
34                 'mouse-face 'highlight
35                 'help-echo (concat "mouse-1: " mouse-help)
36                 'keymap map)))
37
38 (defun elopher-format-i (display-string)
39   (elopher-type-margin)
40   (insert (propertize display-string 'face '(foreground-color . "white")))
41   (insert "\n"))
42
43 (defun elopher-format-0 (display-string selector hostname port)
44   (elopher-type-margin "T")
45   (insert (propertize
46            (elopher-make-clickable display-string
47                                   `(lambda () (interactive)
48                                      (elopher-get-text ,hostname ,port ,selector))
49                                   (format "open \"%s\" on %s port %s"
50                                           selector hostname port))
51            'face '(foreground-color . "gray")))
52   (insert "\n"))
53
54 (defun elopher-format-1 (display-string selector hostname port)
55   (elopher-type-margin "/")
56   (insert (propertize
57            (elopher-make-clickable display-string
58                                    `(lambda () (interactive)
59                                       (elopher-get-index ,hostname ,port ,selector))
60                                    (format "follow link to \"%s\" on %s port %s"
61                                            selector hostname port))
62            'face '(foreground-color . "cyan")))
63   (insert "\n"))
64
65 (defun elopher-process-record (line)
66   (let* ((type (elt line 0))
67          (fields (split-string (substring line 1) "\t"))
68          (g-display-string (elt fields 0))
69          (g-selector (elt fields 1))
70          (g-hostname (elt fields 2))
71          (g-port (elt fields 3)))
72     (pcase type
73       (?i (elopher-format-i g-display-string))
74       (?0 (elopher-format-0 g-display-string g-selector g-hostname g-port))
75       (?1 (elopher-format-1 g-display-string g-selector g-hostname g-port)))))
76
77 (defvar elopher-incomplete-record "")
78
79 (defun elopher-process-complete-records (string)
80   (let* ((til-now (string-join (list elopher-incomplete-record string)))
81          (lines (split-string til-now "\r\n")))
82     (dotimes (idx (length lines))
83       (if (< idx (- (length lines) 1))
84           (elopher-process-record (elt lines idx))
85         (setq elopher-incomplete-record (elt lines idx))))))
86
87 (defun elopher-index-filter (proc string)
88   (with-current-buffer (get-buffer "*elopher*")
89     (let ((marker (process-mark proc)))
90       (if (not (marker-position marker))
91           (set-marker marker 0 (current-buffer)))
92       (save-excursion
93         (goto-char marker)
94         (elopher-process-complete-records string)
95         (set-marker marker (point))))))
96     
97 (defun elopher-get-index (host &optional port path)
98   (switch-to-buffer "*elopher*")
99   (erase-buffer)
100   (make-network-process
101    :name "elopher-process"
102    :host host
103    :service (if port port 70)
104    :filter #'elopher-index-filter)
105   (process-send-string "elopher-process" (concat path "\n")))
106
107 (defun elopher-text-filter (proc string)
108   (with-current-buffer (get-buffer "*elopher*")
109     (let ((marker (process-mark proc)))
110       (if (not (marker-position marker))
111           (set-marker marker 0 (current-buffer)))
112       (save-excursion
113         (goto-char marker)
114         (insert string)
115         (set-marker marker (point))))))
116
117 (defun elopher-get-text (host port selector)
118   (switch-to-buffer "*elopher*")
119   (erase-buffer)
120   (make-network-process
121    :name "elopher-process"
122    :host host
123    :service port
124    :filter #'elopher-text-filter)
125   (process-send-string "elopher-process" (concat selector "\n")))
126
127 (defun elopher ()
128   "Start gopher client."
129   (interactive)
130   (elopher-get-index (read-from-minibuffer "Gopher host: ") 70))
131
132 (elopher-get-index "cosmic.voyage")
133 ;; (elopher-get-index "gopher.floodgap.com")
134 ;; (elopher-get-index "maurits.id.au")
135
136 (defun elopher-quit ()
137   (interactive)
138   (kill-buffer "*elopher*"))
139
140 ;;; elopher.el ends here