summaryrefslogtreecommitdiff
path: root/lisp/net/dictionary-connection.el
blob: 8ad4fe4e6379be08ed13ef15302ec33bc4cdc78d (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
;;; dictionary-connection.el --- TCP-based client connection for dictionary  -*- lexical-binding:t -*-

;; Copyright (C) 2021 Free Software Foundation, Inc.

;; Author: Torsten Hilbrich <torsten.hilbrich@gmx.net>
;; Keywords: network

;; This file is part of GNU Emacs.

;; GNU Emacs is free software: you can redistribute it and/or modify
;; it under the terms of the GNU General Public License as published by
;; the Free Software Foundation, either version 3 of the License, or
;; (at your option) any later version.

;; GNU Emacs is distributed in the hope that it will be useful,
;; but WITHOUT ANY WARRANTY; without even the implied warranty of
;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
;; GNU General Public License for more details.

;; You should have received a copy of the GNU General Public License
;; along with GNU Emacs.  If not, see <https://www.gnu.org/licenses/>.

;;; Commentary:

;; dictionary-connection allows to handle TCP-based connections in
;; client mode where text-based information are exchanged.  There is
;; special support for handling CR LF (and the usual CR LF . CR LF
;; terminater).

;;; Code:

(defsubst dictionary-connection-p (connection)
  "Return non-nil if CONNECTION is a connection object."
  (get connection 'connection))

(defsubst dictionary-connection-read-point (connection)
  "Return the read point of the CONNECTION object."
  (get connection 'dictionary-connection-read-point))

(defsubst dictionary-connection-process (connection)
  "Return the process of the CONNECTION object."
  (get connection 'dictionary-connection-process))

(defsubst dictionary-connection-buffer (connection)
  "Return the buffer of the CONNECTION object."
  (get connection 'dictionary-connection-buffer))

(defsubst dictionary-connection-set-read-point (connection point)
  "Set the read-point for CONNECTION to POINT."
  (put connection 'dictionary-connection-read-point point))

(defsubst dictionary-connection-set-process (connection process)
  "Set the process for CONNECTION to PROCESS."
  (put connection 'dictionary-connection-process process))

(defsubst dictionary-connection-set-buffer (connection buffer)
  "Set the buffer for CONNECTION to BUFFER."
  (put connection 'dictionary-connection-buffer buffer))

(defun dictionary-connection-create-data (buffer process point)
  "Create a new connection data based on BUFFER, PROCESS, and POINT."
  (let ((connection (make-symbol "connection")))
    (put connection 'connection t)
    (dictionary-connection-set-read-point connection point)
    (dictionary-connection-set-process connection process)
    (dictionary-connection-set-buffer connection buffer)
    connection))

(defun dictionary-connection-open (server port)
  "Open a connection to SERVER at PORT.
A data structure identifing the connection is returned"

  (let ((process-buffer (generate-new-buffer (format " connection to %s:%s"
						     server
						     port)))
	(process))
    (with-current-buffer process-buffer
      (setq process (open-network-stream "connection" process-buffer
					 server port))
      (dictionary-connection-create-data process-buffer process (point-min)))))

(defun dictionary-connection-status (connection)
  "Return the status of the CONNECTION.
Possible return values are the symbols:
nil: argument is no connection object
'none: argument has no connection
'up: connection is open and buffer is existing
'down: connection is closed
'alone: connection is not associated with a buffer"
  (when (dictionary-connection-p connection)
    (let ((process (dictionary-connection-process connection))
          (buffer (dictionary-connection-buffer connection)))
      (if (not process)
          'none
        (if (not (buffer-live-p buffer))
            'alone
          (if (not (eq (process-status process) 'open))
              'down
            'up))))))

(defun dictionary-connection-close (connection)
  "Force closing of the CONNECTION."
  (when (dictionary-connection-p connection)
    (let ((buffer (dictionary-connection-buffer connection))
          (process (dictionary-connection-process connection)))
      (if process
          (delete-process process))
      (if buffer
          (kill-buffer buffer))

      (dictionary-connection-set-process connection nil)
      (dictionary-connection-set-buffer connection nil))))

(defun dictionary-connection-send (connection data)
  "Send DATA to the process stored in CONNECTION."
  (unless (eq (dictionary-connection-status connection) 'up)
    (error "Connection is not up"))
  (with-current-buffer (dictionary-connection-buffer connection)
    (goto-char (point-max))
    (dictionary-connection-set-read-point connection (point))
    (process-send-string (dictionary-connection-process connection) data)))

(defun dictionary-connection-send-crlf (connection data)
  "Send DATA together with CRLF to the process found in CONNECTION."
  (dictionary-connection-send connection (concat data "\r\n")))

(defun dictionary-connection-read (connection delimiter)
  "Read data from CONNECTION until DELIMITER is found inside the buffer."
  (unless (eq (dictionary-connection-status connection) 'up)
    (error "Connection is not up"))
  (let ((case-fold-search nil)
	match-end)
    (with-current-buffer (dictionary-connection-buffer connection)
      (goto-char (dictionary-connection-read-point connection))
      ;; Wait until there is enough data
      (while (not (search-forward-regexp delimiter nil t))
	(accept-process-output (dictionary-connection-process connection) 3)
	(goto-char (dictionary-connection-read-point connection)))
      (setq match-end (point))
      ;; Return the result
      (let ((result (buffer-substring (dictionary-connection-read-point connection)
				      match-end)))
	(dictionary-connection-set-read-point connection match-end)
	result))))

(defun dictionary-connection-read-crlf (connection)
  "Read from CONNECTION until a line is completed with CRLF."
  (dictionary-connection-read connection "\015?\012"))

(defun dictionary-connection-read-to-point (connection)
  "Read from CONNECTION until an end of entry is encountered.
End of entry is a decimal point found on a line by itself."
  (dictionary-connection-read connection "\015?\012[.]\015?\012"))

(provide 'dictionary-connection)
;;; dictionary-connection.el ends here