clucumber 0.1.1 → 0.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (139) hide show
  1. data/LICENSE +1 -1
  2. data/README.md +4 -9
  3. data/lib/clucumber/clucumber-bootstrap.lisp +32 -0
  4. data/lib/clucumber/vendor/cl-interpol/alias.lisp +55 -0
  5. data/lib/clucumber/vendor/cl-interpol/cl-interpol.asd +56 -0
  6. data/lib/clucumber/vendor/cl-interpol/load.lisp +53 -0
  7. data/lib/clucumber/vendor/cl-interpol/packages.lisp +40 -0
  8. data/lib/clucumber/vendor/cl-interpol/read.lisp +716 -0
  9. data/lib/clucumber/vendor/cl-interpol/specials.lisp +113 -0
  10. data/lib/clucumber/vendor/cl-interpol/test/packages.lisp +34 -0
  11. data/lib/clucumber/vendor/cl-interpol/test/tests.lisp +128 -0
  12. data/lib/clucumber/vendor/cl-interpol/test.lisp +177 -0
  13. data/lib/clucumber/vendor/cl-interpol/test2.lisp +6254 -0
  14. data/lib/clucumber/vendor/cl-interpol/unicode.lisp +13912 -0
  15. data/lib/clucumber/vendor/cl-interpol/util.lisp +122 -0
  16. data/lib/clucumber/vendor/cl-ppcre/api.lisp +1262 -0
  17. data/lib/clucumber/vendor/cl-ppcre/charmap.lisp +152 -0
  18. data/lib/clucumber/vendor/cl-ppcre/charset.lisp +242 -0
  19. data/lib/clucumber/vendor/cl-ppcre/chartest.lisp +98 -0
  20. data/lib/clucumber/vendor/cl-ppcre/cl-ppcre-test.asd +34 -0
  21. data/lib/clucumber/vendor/cl-ppcre/cl-ppcre-unicode/packages.lisp +38 -0
  22. data/lib/clucumber/vendor/cl-ppcre/cl-ppcre-unicode/resolver.lisp +61 -0
  23. data/lib/clucumber/vendor/cl-ppcre/cl-ppcre-unicode.asd +58 -0
  24. data/lib/clucumber/vendor/cl-ppcre/cl-ppcre.asd +79 -0
  25. data/lib/clucumber/vendor/cl-ppcre/closures.lisp +471 -0
  26. data/lib/clucumber/vendor/cl-ppcre/convert.lisp +875 -0
  27. data/lib/clucumber/vendor/cl-ppcre/errors.lisp +84 -0
  28. data/lib/clucumber/vendor/cl-ppcre/lexer.lisp +737 -0
  29. data/lib/clucumber/vendor/cl-ppcre/lispworks-defsystem.lisp +57 -0
  30. data/lib/clucumber/vendor/cl-ppcre/load.lisp +67 -0
  31. data/lib/clucumber/vendor/cl-ppcre/optimize.lisp +578 -0
  32. data/lib/clucumber/vendor/cl-ppcre/packages.lisp +68 -0
  33. data/lib/clucumber/vendor/cl-ppcre/parser.lisp +319 -0
  34. data/lib/clucumber/vendor/cl-ppcre/ppcre-tests.lisp +269 -0
  35. data/lib/clucumber/vendor/cl-ppcre/regex-class-util.lisp +555 -0
  36. data/lib/clucumber/vendor/cl-ppcre/regex-class.lisp +259 -0
  37. data/lib/clucumber/vendor/cl-ppcre/repetition-closures.lisp +833 -0
  38. data/lib/clucumber/vendor/cl-ppcre/scanner.lisp +506 -0
  39. data/lib/clucumber/vendor/cl-ppcre/specials.lisp +172 -0
  40. data/lib/clucumber/vendor/cl-ppcre/test/packages.lisp +37 -0
  41. data/lib/clucumber/vendor/cl-ppcre/test/perl-tests.lisp +150 -0
  42. data/lib/clucumber/vendor/cl-ppcre/test/tests.lisp +159 -0
  43. data/lib/clucumber/vendor/cl-ppcre/test/unicode-tests.lisp +80 -0
  44. data/lib/clucumber/vendor/cl-ppcre/util.lisp +201 -0
  45. data/lib/clucumber/vendor/cl-unicode/alias.lisp +135 -0
  46. data/lib/clucumber/vendor/cl-unicode/api.lisp +412 -0
  47. data/lib/clucumber/vendor/cl-unicode/build/char-info.lisp +133 -0
  48. data/lib/clucumber/vendor/cl-unicode/build/dump.lisp +239 -0
  49. data/lib/clucumber/vendor/cl-unicode/build/read.lisp +280 -0
  50. data/lib/clucumber/vendor/cl-unicode/build/util.lisp +182 -0
  51. data/lib/clucumber/vendor/cl-unicode/cl-unicode.asd +90 -0
  52. data/lib/clucumber/vendor/cl-unicode/conditions.lisp +54 -0
  53. data/lib/clucumber/vendor/cl-unicode/derived.lisp +120 -0
  54. data/lib/clucumber/vendor/cl-unicode/hash-tables.lisp +20 -0
  55. data/lib/clucumber/vendor/cl-unicode/lists.lisp +10 -0
  56. data/lib/clucumber/vendor/cl-unicode/methods.lisp +15 -0
  57. data/lib/clucumber/vendor/cl-unicode/packages.lisp +76 -0
  58. data/lib/clucumber/vendor/cl-unicode/specials.lisp +135 -0
  59. data/lib/clucumber/vendor/cl-unicode/test/packages.lisp +34 -0
  60. data/lib/clucumber/vendor/cl-unicode/test/tests.lisp +150 -0
  61. data/lib/clucumber/vendor/cl-unicode/test-functions.lisp +94 -0
  62. data/lib/clucumber/vendor/cl-unicode/util.lisp +274 -0
  63. data/lib/clucumber/vendor/lift/compare/fiveam-tests.lisp +14 -0
  64. data/lib/clucumber/vendor/lift/compare/lift-tests.lisp +13 -0
  65. data/lib/clucumber/vendor/lift/compare/rt-tests.lisp +16 -0
  66. data/lib/clucumber/vendor/lift/compare/xlunit-tests.lisp +15 -0
  67. data/lib/clucumber/vendor/lift/dev/changes.lisp +61 -0
  68. data/lib/clucumber/vendor/lift/dev/config.lisp +354 -0
  69. data/lib/clucumber/vendor/lift/dev/copy-file.lisp +117 -0
  70. data/lib/clucumber/vendor/lift/dev/introspection.lisp +232 -0
  71. data/lib/clucumber/vendor/lift/dev/lift-interface.lisp +56 -0
  72. data/lib/clucumber/vendor/lift/dev/lift-notes.lisp +202 -0
  73. data/lib/clucumber/vendor/lift/dev/lift-randomized.lisp +45 -0
  74. data/lib/clucumber/vendor/lift/dev/lift.lisp +2383 -0
  75. data/lib/clucumber/vendor/lift/dev/macros.lisp +229 -0
  76. data/lib/clucumber/vendor/lift/dev/measuring.lisp +156 -0
  77. data/lib/clucumber/vendor/lift/dev/packages.lisp +161 -0
  78. data/lib/clucumber/vendor/lift/dev/port.lisp +151 -0
  79. data/lib/clucumber/vendor/lift/dev/prototypes.lisp +282 -0
  80. data/lib/clucumber/vendor/lift/dev/random-testing.lisp +124 -0
  81. data/lib/clucumber/vendor/lift/dev/report-locations.lisp +13 -0
  82. data/lib/clucumber/vendor/lift/dev/reports.lisp +916 -0
  83. data/lib/clucumber/vendor/lift/dev/utilities.lisp +242 -0
  84. data/lib/clucumber/vendor/lift/docs/package.lisp +6 -0
  85. data/lib/clucumber/vendor/lift/docs/setup.lisp +17 -0
  86. data/lib/clucumber/vendor/lift/examples/basic-examples.lisp +289 -0
  87. data/lib/clucumber/vendor/lift/examples/random-testing.lisp +32 -0
  88. data/lib/clucumber/vendor/lift/lift-documentation.asd +28 -0
  89. data/lib/clucumber/vendor/lift/lift-test.asd +35 -0
  90. data/lib/clucumber/vendor/lift/lift.asd +77 -0
  91. data/lib/clucumber/vendor/lift/test/equality-tests.lisp +40 -0
  92. data/lib/clucumber/vendor/lift/test/finding-tests.lisp +32 -0
  93. data/lib/clucumber/vendor/lift/test/lift-test.lisp +783 -0
  94. data/lib/clucumber/vendor/lift/test/order-of-operations.lisp +54 -0
  95. data/lib/clucumber/vendor/lift/test/packages.lisp +17 -0
  96. data/lib/clucumber/vendor/lift/test/test-config-files.lisp +20 -0
  97. data/lib/clucumber/vendor/lift/test/test-dynamic-variables.lisp +57 -0
  98. data/lib/clucumber/vendor/lift/test/test-maximum-problems.lisp +74 -0
  99. data/lib/clucumber/vendor/lift/test/test-prototypes.lisp +278 -0
  100. data/lib/clucumber/vendor/lift/test/test-timeout.lisp +37 -0
  101. data/lib/clucumber/vendor/lift/test/tests-in-progress.lisp +62 -0
  102. data/lib/clucumber/vendor/lift/test/testsuite-expects.lisp +60 -0
  103. data/lib/clucumber/vendor/lift/timeout/package.lisp +13 -0
  104. data/lib/clucumber/vendor/lift/timeout/with-timeout.lisp +123 -0
  105. data/lib/clucumber/vendor/lift/website/stuff/Temp.lisp +34 -0
  106. data/lib/clucumber/vendor/st-json/st-json.asd +3 -0
  107. data/lib/clucumber/vendor/st-json/st-json.lisp +310 -0
  108. data/lib/clucumber/vendor/trivial-backtrace/dev/backtrace.lisp +127 -0
  109. data/lib/clucumber/vendor/trivial-backtrace/dev/fallback.lisp +10 -0
  110. data/lib/clucumber/vendor/trivial-backtrace/dev/map-backtrace.lisp +103 -0
  111. data/lib/clucumber/vendor/trivial-backtrace/dev/mucking.lisp +75 -0
  112. data/lib/clucumber/vendor/trivial-backtrace/dev/packages.lisp +13 -0
  113. data/lib/clucumber/vendor/trivial-backtrace/dev/utilities.lisp +104 -0
  114. data/lib/clucumber/vendor/trivial-backtrace/test/packages.lisp +5 -0
  115. data/lib/clucumber/vendor/trivial-backtrace/test/test-setup.lisp +4 -0
  116. data/lib/clucumber/vendor/trivial-backtrace/test/tests.lisp +16 -0
  117. data/lib/clucumber/vendor/trivial-backtrace/trivial-backtrace-test.asd +22 -0
  118. data/lib/clucumber/vendor/trivial-backtrace/trivial-backtrace.asd +35 -0
  119. data/lib/clucumber/vendor/usocket/backend/allegro.lisp +199 -0
  120. data/lib/clucumber/vendor/usocket/backend/armedbear.lisp +491 -0
  121. data/lib/clucumber/vendor/usocket/backend/clisp.lisp +260 -0
  122. data/lib/clucumber/vendor/usocket/backend/cmucl.lisp +266 -0
  123. data/lib/clucumber/vendor/usocket/backend/lispworks.lisp +741 -0
  124. data/lib/clucumber/vendor/usocket/backend/mcl.lisp +369 -0
  125. data/lib/clucumber/vendor/usocket/backend/openmcl.lisp +206 -0
  126. data/lib/clucumber/vendor/usocket/backend/sbcl.lisp +424 -0
  127. data/lib/clucumber/vendor/usocket/backend/scl.lisp +261 -0
  128. data/lib/clucumber/vendor/usocket/condition.lisp +227 -0
  129. data/lib/clucumber/vendor/usocket/package.lisp +82 -0
  130. data/lib/clucumber/vendor/usocket/server.lisp +45 -0
  131. data/lib/clucumber/vendor/usocket/test/package.lisp +13 -0
  132. data/lib/clucumber/vendor/usocket/test/test-usocket.lisp +166 -0
  133. data/lib/clucumber/vendor/usocket/usocket-test.asd +26 -0
  134. data/lib/clucumber/vendor/usocket/usocket.asd +37 -0
  135. data/lib/clucumber/vendor/usocket/usocket.lisp +542 -0
  136. data/lib/clucumber/vendor/usocket/vendor/kqueue.lisp +1 -0
  137. data/lib/clucumber/vendor/usocket/vendor/split-sequence.lisp +245 -0
  138. data/lib/clucumber.rb +29 -7
  139. metadata +151 -5
@@ -0,0 +1,741 @@
1
+ ;;;; $Id: lispworks.lisp 521 2010-02-21 03:38:51Z ctian $
2
+ ;;;; $URL: svn://common-lisp.net/project/usocket/svn/usocket/trunk/backend/lispworks.lisp $
3
+
4
+ ;;;; See LICENSE for licensing information.
5
+
6
+ (in-package :usocket)
7
+
8
+ (eval-when (:compile-toplevel :load-toplevel :execute)
9
+ (require "comm")
10
+
11
+ #+lispworks3
12
+ (error "LispWorks 3 is not supported by USOCKET."))
13
+
14
+ ;;; ---------------------------------------------------------------------------
15
+ ;;; Warn if multiprocessing is not running on Lispworks
16
+
17
+ (defun check-for-multiprocessing-started (&optional errorp)
18
+ (unless mp:*current-process*
19
+ (funcall (if errorp 'error 'warn)
20
+ "You must start multiprocessing on Lispworks by calling~
21
+ ~%~3t(~s)~
22
+ ~%for ~s function properly."
23
+ 'mp:initialize-multiprocessing
24
+ 'wait-for-input)))
25
+
26
+ (eval-when (:load-toplevel :execute)
27
+ (check-for-multiprocessing-started))
28
+
29
+ #+win32
30
+ (eval-when (:load-toplevel :execute)
31
+ (fli:register-module "ws2_32")
32
+ (comm::ensure-sockets))
33
+
34
+ (fli:define-foreign-function (get-host-name-internal "gethostname" :source)
35
+ ((return-string (:reference-return (:ef-mb-string :limit 257)))
36
+ (namelen :int))
37
+ :lambda-list (&aux (namelen 256) return-string)
38
+ :result-type :int
39
+ #+win32 :module
40
+ #+win32 "ws2_32")
41
+
42
+ (defun get-host-name ()
43
+ (multiple-value-bind (retcode name)
44
+ (get-host-name-internal)
45
+ (when (= 0 retcode)
46
+ name)))
47
+
48
+ #+win32
49
+ (defun remap-maybe-for-win32 (z)
50
+ (mapcar #'(lambda (x)
51
+ (cons (mapcar #'(lambda (y)
52
+ (+ 10000 y))
53
+ (car x))
54
+ (cdr x)))
55
+ z))
56
+
57
+ (defparameter +lispworks-error-map+
58
+ #+win32
59
+ (append (remap-maybe-for-win32 +unix-errno-condition-map+)
60
+ (remap-maybe-for-win32 +unix-errno-error-map+))
61
+ #-win32
62
+ (append +unix-errno-condition-map+
63
+ +unix-errno-error-map+))
64
+
65
+ (defun raise-or-signal-socket-error (errno socket)
66
+ (let ((usock-err
67
+ (cdr (assoc errno +lispworks-error-map+ :test #'member))))
68
+ (if usock-err
69
+ (if (subtypep usock-err 'error)
70
+ (error usock-err :socket socket)
71
+ (signal usock-err :socket))
72
+ (error 'unknown-error
73
+ :socket socket
74
+ :real-condition nil))))
75
+
76
+ (defun raise-usock-err (errno socket &optional condition)
77
+ (let* ((usock-err
78
+ (cdr (assoc errno +lispworks-error-map+
79
+ :test #'member))))
80
+ (if usock-err
81
+ (if (subtypep usock-err 'error)
82
+ (error usock-err :socket socket)
83
+ (signal usock-err :socket))
84
+ (error 'unknown-error
85
+ :socket socket
86
+ :real-error condition))))
87
+
88
+ (defun handle-condition (condition &optional (socket nil))
89
+ "Dispatch correct usocket condition."
90
+ (typecase condition
91
+ (simple-error (destructuring-bind (&optional host port err-msg errno)
92
+ (simple-condition-format-arguments condition)
93
+ (declare (ignore host port err-msg))
94
+ (raise-usock-err errno socket condition)))))
95
+
96
+ (defconstant *socket_sock_dgram* 2
97
+ "Connectionless, unreliable datagrams of fixed maximum length.")
98
+
99
+ (defconstant *sockopt_so_rcvtimeo*
100
+ #+(not linux) #x1006
101
+ #+linux 20
102
+ "Socket receive timeout")
103
+
104
+ (fli:define-c-struct timeval
105
+ (tv-sec :long)
106
+ (tv-usec :long))
107
+
108
+ ;;; ssize_t
109
+ ;;; recvfrom(int socket, void *restrict buffer, size_t length, int flags,
110
+ ;;; struct sockaddr *restrict address, socklen_t *restrict address_len);
111
+ (fli:define-foreign-function (%recvfrom "recvfrom" :source)
112
+ ((socket :int)
113
+ (buffer (:pointer (:unsigned :byte)))
114
+ (length :int)
115
+ (flags :int)
116
+ (address (:pointer (:struct comm::sockaddr)))
117
+ (address-len (:pointer :int)))
118
+ :result-type :int
119
+ #+win32 :module
120
+ #+win32 "ws2_32")
121
+
122
+ ;;; ssize_t
123
+ ;;; sendto(int socket, const void *buffer, size_t length, int flags,
124
+ ;;; const struct sockaddr *dest_addr, socklen_t dest_len);
125
+ (fli:define-foreign-function (%sendto "sendto" :source)
126
+ ((socket :int)
127
+ (buffer (:pointer (:unsigned :byte)))
128
+ (length :int)
129
+ (flags :int)
130
+ (address (:pointer (:struct comm::sockaddr)))
131
+ (address-len :int))
132
+ :result-type :int
133
+ #+win32 :module
134
+ #+win32 "ws2_32")
135
+
136
+ #-win32
137
+ (defun set-socket-receive-timeout (socket-fd seconds)
138
+ "Set socket option: RCVTIMEO, argument seconds can be a float number"
139
+ (declare (type integer socket-fd)
140
+ (type number seconds))
141
+ (multiple-value-bind (sec usec) (truncate seconds)
142
+ (fli:with-dynamic-foreign-objects ((timeout (:struct timeval)))
143
+ (fli:with-foreign-slots (tv-sec tv-usec) timeout
144
+ (setf tv-sec sec
145
+ tv-usec (truncate (* 1000000 usec)))
146
+ (if (zerop (comm::setsockopt socket-fd
147
+ comm::*sockopt_sol_socket*
148
+ *sockopt_so_rcvtimeo*
149
+ (fli:copy-pointer timeout
150
+ :type '(:pointer :void))
151
+ (fli:size-of '(:struct timeval))))
152
+ seconds)))))
153
+
154
+ #+win32
155
+ (defun set-socket-receive-timeout (socket-fd seconds)
156
+ "Set socket option: RCVTIMEO, argument seconds can be a float number.
157
+ On win32, you must bind the socket before use this function."
158
+ (declare (type integer socket-fd)
159
+ (type number seconds))
160
+ (fli:with-dynamic-foreign-objects ((timeout :int))
161
+ (setf (fli:dereference timeout)
162
+ (truncate (* 1000 seconds)))
163
+ (if (zerop (comm::setsockopt socket-fd
164
+ comm::*sockopt_sol_socket*
165
+ *sockopt_so_rcvtimeo*
166
+ (fli:copy-pointer timeout
167
+ :type '(:pointer :char))
168
+ (fli:size-of :int)))
169
+ seconds)))
170
+
171
+ #-win32
172
+ (defmethod get-socket-receive-timeout (socket-fd)
173
+ "Get socket option: RCVTIMEO, return value is a float number"
174
+ (declare (type integer socket-fd))
175
+ (fli:with-dynamic-foreign-objects ((timeout (:struct timeval))
176
+ (len :int))
177
+ (comm::getsockopt socket-fd
178
+ comm::*sockopt_sol_socket*
179
+ *sockopt_so_rcvtimeo*
180
+ (fli:copy-pointer timeout
181
+ :type '(:pointer :void))
182
+ len)
183
+ (fli:with-foreign-slots (tv-sec tv-usec) timeout
184
+ (float (+ tv-sec (/ tv-usec 1000000))))))
185
+
186
+ #+win32
187
+ (defmethod get-socket-receive-timeout (socket-fd)
188
+ "Get socket option: RCVTIMEO, return value is a float number"
189
+ (declare (type integer socket-fd))
190
+ (fli:with-dynamic-foreign-objects ((timeout :int)
191
+ (len :int))
192
+ (comm::getsockopt socket-fd
193
+ comm::*sockopt_sol_socket*
194
+ *sockopt_so_rcvtimeo*
195
+ (fli:copy-pointer timeout
196
+ :type '(:pointer :void))
197
+ len)
198
+ (float (/ (fli:dereference timeout) 1000))))
199
+
200
+ (defun open-udp-socket (&key local-address local-port read-timeout)
201
+ "Open a unconnected UDP socket.
202
+ For binding on address ANY(*), just not set LOCAL-ADDRESS (NIL),
203
+ for binding on random free unused port, set LOCAL-PORT to 0."
204
+ (let ((socket-fd (comm::socket comm::*socket_af_inet* *socket_sock_dgram* comm::*socket_pf_unspec*)))
205
+ (if socket-fd
206
+ (progn
207
+ (when read-timeout (set-socket-receive-timeout socket-fd read-timeout))
208
+ (if local-port
209
+ (fli:with-dynamic-foreign-objects ((client-addr (:struct comm::sockaddr_in)))
210
+ (comm::initialize-sockaddr_in client-addr comm::*socket_af_inet*
211
+ local-address local-port "udp")
212
+ (if (comm::bind socket-fd
213
+ (fli:copy-pointer client-addr :type '(:struct comm::sockaddr))
214
+ (fli:pointer-element-size client-addr))
215
+ ;; success, return socket fd
216
+ socket-fd
217
+ (progn
218
+ (comm::close-socket socket-fd)
219
+ (error "cannot bind"))))
220
+ socket-fd))
221
+ (error "cannot create socket"))))
222
+
223
+ (defun connect-to-udp-server (hostname service
224
+ &key local-address local-port read-timeout)
225
+ "Something like CONNECT-TO-TCP-SERVER"
226
+ (let ((socket-fd (open-udp-socket :local-address local-address
227
+ :local-port local-port
228
+ :read-timeout read-timeout)))
229
+ (if socket-fd
230
+ (fli:with-dynamic-foreign-objects ((server-addr (:struct comm::sockaddr_in)))
231
+ ;; connect to remote address/port
232
+ (comm::initialize-sockaddr_in server-addr comm::*socket_af_inet* hostname service "udp")
233
+ (if (comm::connect socket-fd
234
+ (fli:copy-pointer server-addr :type '(:struct comm::sockaddr))
235
+ (fli:pointer-element-size server-addr))
236
+ ;; success, return socket fd
237
+ socket-fd
238
+ ;; fail, close socket and return nil
239
+ (progn
240
+ (comm::close-socket socket-fd)
241
+ (error "cannot connect"))))
242
+ (error "cannot create socket"))))
243
+
244
+ ;; Register a special free action for closing datagram usocket when being GCed
245
+ (defun usocket-special-free-action (object)
246
+ (when (and (typep object 'datagram-usocket)
247
+ (%open-p object))
248
+ (socket-close object)))
249
+
250
+ (eval-when (:load-toplevel :execute)
251
+ (hcl:add-special-free-action 'usocket-special-free-action))
252
+
253
+ (defun socket-connect (host port &key (protocol :stream) (element-type 'base-char)
254
+ timeout deadline (nodelay t nodelay-specified)
255
+ local-host (local-port #+win32 *auto-port* #-win32 nil))
256
+ (declare (ignorable nodelay))
257
+
258
+ ;; What's the meaning of this keyword?
259
+ (when deadline
260
+ (unimplemented 'deadline 'socket-connect))
261
+
262
+ #+(and lispworks4 (not lispworks4.4)) ; < 4.4.5
263
+ (when timeout
264
+ (unsupported 'timeout 'socket-connect :minimum "LispWorks 4.4.5"))
265
+
266
+ #+(or lispworks4 lispworks5.0) ; < 5.1
267
+ (when nodelay-specified
268
+ (unsupported 'nodelay 'socket-connect :minimum "LispWorks 5.1"))
269
+
270
+ #+lispworks4 #+lispworks4
271
+ (when local-host
272
+ (unsupported 'local-host 'socket-connect :minimum "LispWorks 5.0"))
273
+ (when local-port
274
+ (unsupported 'local-port 'socket-connect :minimum "LispWorks 5.0"))
275
+
276
+ (ecase protocol
277
+ (:stream
278
+ (let ((hostname (host-to-hostname host))
279
+ (stream))
280
+ (setf stream
281
+ (with-mapped-conditions ()
282
+ (comm:open-tcp-stream hostname port
283
+ :element-type element-type
284
+ #-(and lispworks4 (not lispworks4.4)) ; >= 4.4.5
285
+ #-(and lispworks4 (not lispworks4.4))
286
+ :timeout timeout
287
+ #-lispworks4 #-lispworks4
288
+ #-lispworks4 #-lispworks4
289
+ :local-address (when local-host (host-to-hostname local-host))
290
+ :local-port local-port
291
+ #-(or lispworks4 lispworks5.0) ; >= 5.1
292
+ #-(or lispworks4 lispworks5.0)
293
+ :nodelay nodelay)))
294
+ (if stream
295
+ (make-stream-socket :socket (comm:socket-stream-socket stream)
296
+ :stream stream)
297
+ (error 'unknown-error))))
298
+ (:datagram
299
+ (let ((usocket (make-datagram-socket
300
+ (if (and host port)
301
+ (connect-to-udp-server host port
302
+ :local-address local-host
303
+ :local-port local-port)
304
+ (open-udp-socket :local-address local-host
305
+ :local-port local-port))
306
+ :connected-p t)))
307
+ (hcl:flag-special-free-action usocket)
308
+ usocket))))
309
+
310
+ (defun socket-listen (host port
311
+ &key reuseaddress
312
+ (reuse-address nil reuse-address-supplied-p)
313
+ (backlog 5)
314
+ (element-type 'base-char))
315
+ #+lispworks4.1
316
+ (unsupported 'host 'socket-listen :minimum "LispWorks 4.0 or newer than 4.1")
317
+ #+lispworks4.1
318
+ (unsupported 'backlog 'socket-listen :minimum "LispWorks 4.0 or newer than 4.1")
319
+
320
+ (let* ((reuseaddress (if reuse-address-supplied-p reuse-address reuseaddress))
321
+ (comm::*use_so_reuseaddr* reuseaddress)
322
+ (hostname (host-to-hostname host))
323
+ (sock (with-mapped-conditions ()
324
+ #-lispworks4.1 (comm::create-tcp-socket-for-service
325
+ port :address hostname :backlog backlog)
326
+ #+lispworks4.1 (comm::create-tcp-socket-for-service port))))
327
+ (make-stream-server-socket sock :element-type element-type)))
328
+
329
+ (defmethod socket-accept ((usocket stream-server-usocket) &key element-type)
330
+ (let* ((sock (with-mapped-conditions (usocket)
331
+ (comm::get-fd-from-socket (socket usocket))))
332
+ (stream (make-instance 'comm:socket-stream
333
+ :socket sock
334
+ :direction :io
335
+ :element-type (or element-type
336
+ (element-type usocket)))))
337
+ #+win32
338
+ (when sock
339
+ (setf (%ready-p usocket) nil))
340
+ (make-stream-socket :socket sock :stream stream)))
341
+
342
+ ;; Sockets and their streams are different objects
343
+ ;; close the stream in order to make sure buffers
344
+ ;; are correctly flushed and the socket closed.
345
+ (defmethod socket-close ((usocket stream-usocket))
346
+ "Close socket."
347
+ (when (wait-list usocket)
348
+ (remove-waiter (wait-list usocket) usocket))
349
+ (close (socket-stream usocket)))
350
+
351
+ (defmethod socket-close ((usocket usocket))
352
+ (when (wait-list usocket)
353
+ (remove-waiter (wait-list usocket) usocket))
354
+ (with-mapped-conditions (usocket)
355
+ (comm::close-socket (socket usocket))))
356
+
357
+ (defmethod socket-close :after ((socket datagram-usocket))
358
+ "Additional socket-close method for datagram-usocket"
359
+ (setf (%open-p socket) nil))
360
+
361
+ (defvar *message-send-buffer*
362
+ (make-array +max-datagram-packet-size+
363
+ :element-type '(unsigned-byte 8)
364
+ :allocation :static))
365
+
366
+ (defvar *message-send-lock*
367
+ (mp:make-lock :name "USOCKET message send lock"))
368
+
369
+ (defun send-message (socket-fd buffer &optional (length (length buffer)) host service)
370
+ "Send message to a socket, using sendto()/send()"
371
+ (declare (type integer socket-fd)
372
+ (type sequence buffer))
373
+ (let ((message *message-send-buffer*))
374
+ (fli:with-dynamic-foreign-objects ((client-addr (:struct comm::sockaddr_in))
375
+ (len :int
376
+ #-(or lispworks4 lispworks5.0) ; <= 5.0
377
+ :initial-element
378
+ (fli:size-of '(:struct comm::sockaddr_in))))
379
+ (fli:with-dynamic-lisp-array-pointer (ptr message :type '(:unsigned :byte))
380
+ (mp:with-lock (*message-send-lock*)
381
+ (replace message buffer :end2 length)
382
+ (if (and host service)
383
+ (progn
384
+ (comm::initialize-sockaddr_in client-addr comm::*socket_af_inet* host service "udp")
385
+ (%sendto socket-fd ptr (min length +max-datagram-packet-size+) 0
386
+ (fli:copy-pointer client-addr :type '(:struct comm::sockaddr))
387
+ (fli:dereference len)))
388
+ (comm::%send socket-fd ptr (min length +max-datagram-packet-size+) 0)))))))
389
+
390
+ (defmethod socket-send ((socket datagram-usocket) buffer length &key host port)
391
+ (let ((s (socket socket)))
392
+ (send-message s buffer length (and host (host-to-hbo host)) port)))
393
+
394
+ (defvar *message-receive-buffer*
395
+ (make-array +max-datagram-packet-size+
396
+ :element-type '(unsigned-byte 8)
397
+ :allocation :static))
398
+
399
+ (defvar *message-receive-lock*
400
+ (mp:make-lock :name "USOCKET message receive lock"))
401
+
402
+ (defun receive-message (socket-fd &optional buffer (length (length buffer))
403
+ &key read-timeout (max-buffer-size +max-datagram-packet-size+))
404
+ "Receive message from socket, read-timeout is a float number in seconds.
405
+
406
+ This function will return 4 values:
407
+ 1. receive buffer
408
+ 2. number of receive bytes
409
+ 3. remote address
410
+ 4. remote port"
411
+ (declare (type integer socket-fd)
412
+ (type sequence buffer))
413
+ (let ((message *message-receive-buffer*)
414
+ old-timeout)
415
+ (fli:with-dynamic-foreign-objects ((client-addr (:struct comm::sockaddr_in))
416
+ (len :int
417
+ #-(or lispworks4 lispworks5.0) ; <= 5.0
418
+ :initial-element
419
+ (fli:size-of '(:struct comm::sockaddr_in))))
420
+ (fli:with-dynamic-lisp-array-pointer (ptr message :type '(:unsigned :byte))
421
+ ;; setup new read timeout
422
+ (when read-timeout
423
+ (setf old-timeout (get-socket-receive-timeout socket-fd))
424
+ (set-socket-receive-timeout socket-fd read-timeout))
425
+ (mp:with-lock (*message-receive-lock*)
426
+ (let ((n (%recvfrom socket-fd ptr max-buffer-size 0
427
+ (fli:copy-pointer client-addr :type '(:struct comm::sockaddr))
428
+ len)))
429
+ ;; restore old read timeout
430
+ (when (and read-timeout (/= old-timeout read-timeout))
431
+ (set-socket-receive-timeout socket-fd old-timeout))
432
+ (if (plusp n)
433
+ (values (if buffer
434
+ (replace buffer message
435
+ :end1 (min length max-buffer-size)
436
+ :end2 (min n max-buffer-size))
437
+ (subseq message 0 (min n max-buffer-size)))
438
+ (min n max-buffer-size)
439
+ (comm::ntohl (fli:foreign-slot-value
440
+ (fli:foreign-slot-value client-addr
441
+ 'comm::sin_addr
442
+ :object-type '(:struct comm::sockaddr_in)
443
+ :type '(:struct comm::in_addr)
444
+ :copy-foreign-object nil)
445
+ 'comm::s_addr
446
+ :object-type '(:struct comm::in_addr)))
447
+ (comm::ntohs (fli:foreign-slot-value client-addr
448
+ 'comm::sin_port
449
+ :object-type '(:struct comm::sockaddr_in)
450
+ :type '(:unsigned :short)
451
+ :copy-foreign-object nil)))
452
+ (values nil n 0 0))))))))
453
+
454
+ (defmethod socket-receive ((socket datagram-usocket) buffer length &key)
455
+ (let ((s (socket socket)))
456
+ (multiple-value-bind (buffer size host port)
457
+ (receive-message s buffer length)
458
+ (values buffer size host port))))
459
+
460
+ (defmethod get-local-name ((usocket usocket))
461
+ (multiple-value-bind
462
+ (address port)
463
+ (comm:get-socket-address (socket usocket))
464
+ (values (hbo-to-vector-quad address) port)))
465
+
466
+ (defmethod get-peer-name ((usocket stream-usocket))
467
+ (multiple-value-bind
468
+ (address port)
469
+ (comm:get-socket-peer-address (socket usocket))
470
+ (values (hbo-to-vector-quad address) port)))
471
+
472
+ (defmethod get-local-address ((usocket usocket))
473
+ (nth-value 0 (get-local-name usocket)))
474
+
475
+ (defmethod get-peer-address ((usocket stream-usocket))
476
+ (nth-value 0 (get-peer-name usocket)))
477
+
478
+ (defmethod get-local-port ((usocket usocket))
479
+ (nth-value 1 (get-local-name usocket)))
480
+
481
+ (defmethod get-peer-port ((usocket stream-usocket))
482
+ (nth-value 1 (get-peer-name usocket)))
483
+
484
+ (defun get-hosts-by-name (name)
485
+ (with-mapped-conditions ()
486
+ (mapcar #'hbo-to-vector-quad
487
+ (comm:get-host-entry name :fields '(:addresses)))))
488
+
489
+ (defun os-socket-handle (usocket)
490
+ (socket usocket))
491
+
492
+ (defun usocket-listen (usocket)
493
+ (if (stream-usocket-p usocket)
494
+ (when (listen (socket-stream usocket))
495
+ usocket)
496
+ (when (comm::socket-listen (socket usocket))
497
+ usocket)))
498
+
499
+ ;;;
500
+ ;;; Non Windows implementation
501
+ ;;; The Windows implementation needs to resort to the Windows API in order
502
+ ;;; to achieve what we want (what we want is waiting without busy-looping)
503
+ ;;;
504
+
505
+ #-win32
506
+ (progn
507
+
508
+ (defun %setup-wait-list (wait-list)
509
+ (declare (ignore wait-list)))
510
+
511
+ (defun %add-waiter (wait-list waiter)
512
+ (declare (ignore wait-list waiter)))
513
+
514
+ (defun %remove-waiter (wait-list waiter)
515
+ (declare (ignore wait-list waiter)))
516
+
517
+ (defun wait-for-input-internal (wait-list &key timeout)
518
+ (with-mapped-conditions ()
519
+ ;; unfortunately, it's impossible to share code between
520
+ ;; non-win32 and win32 platforms...
521
+ ;; Can we have a sane -pref. complete [UDP!?]- API next time, please?
522
+ (dolist (x (wait-list-waiters wait-list))
523
+ (mp:notice-fd (os-socket-handle x)))
524
+ (labels ((wait-function (socks)
525
+ (let (rv)
526
+ (dolist (x socks rv)
527
+ (when (usocket-listen x)
528
+ (setf (state x) :READ
529
+ rv t))))))
530
+ (if timeout
531
+ (mp:process-wait-with-timeout "Waiting for a socket to become active"
532
+ (truncate timeout)
533
+ #'wait-function
534
+ (wait-list-waiters wait-list))
535
+ (mp:process-wait "Waiting for a socket to become active"
536
+ #'wait-function
537
+ (wait-list-waiters wait-list))))
538
+ (dolist (x (wait-list-waiters wait-list))
539
+ (mp:unnotice-fd (os-socket-handle x)))
540
+ wait-list))
541
+
542
+ ) ; end of block
543
+
544
+
545
+ ;;;
546
+ ;;; The Windows side of the story
547
+ ;;; We want to wait without busy looping
548
+ ;;; This code only works in threads which don't have (hidden)
549
+ ;;; windows which need to receive messages. There are workarounds in the Windows API
550
+ ;;; but are those available to 'us'.
551
+ ;;;
552
+
553
+
554
+ #+win32
555
+ (progn
556
+
557
+ ;; LispWorks doesn't provide an interface to wait for a socket
558
+ ;; to become ready (under Win32, that is) meaning that we need
559
+ ;; to resort to system calls to achieve the same thing.
560
+ ;; Luckily, it provides us access to the raw socket handles (as we
561
+ ;; wrote the code above.
562
+
563
+ (defconstant fd-read 1)
564
+ (defconstant fd-read-bit 0)
565
+ (defconstant fd-write 2)
566
+ (defconstant fd-write-bit 1)
567
+ (defconstant fd-oob 4)
568
+ (defconstant fd-oob-bit 2)
569
+ (defconstant fd-accept 8)
570
+ (defconstant fd-accept-bit 3)
571
+ (defconstant fd-connect 16)
572
+ (defconstant fd-connect-bit 4)
573
+ (defconstant fd-close 32)
574
+ (defconstant fd-close-bit 5)
575
+ (defconstant fd-qos 64)
576
+ (defconstant fd-qos-bit 6)
577
+ (defconstant fd-group-qos 128)
578
+ (defconstant fd-group-qos-bit 7)
579
+ (defconstant fd-routing-interface 256)
580
+ (defconstant fd-routing-interface-bit 8)
581
+ (defconstant fd-address-list-change 512)
582
+ (defconstant fd-address-list-change-bit 9)
583
+
584
+ (defconstant fd-max-events 10)
585
+
586
+ (defconstant fionread 1074030207)
587
+
588
+
589
+ ;; Note:
590
+ ;;
591
+ ;; If special finalization has to occur for a given
592
+ ;; system resource (handle), an associated object should
593
+ ;; be created. A special cleanup action should be added
594
+ ;; to the system and a special cleanup action should
595
+ ;; be flagged on all objects created for resources like it
596
+ ;;
597
+ ;; We have 2 functions to do so:
598
+ ;; * hcl:add-special-free-action (function-symbol)
599
+ ;; * hcl:flag-special-free-action (object)
600
+ ;;
601
+ ;; Note that the special free action will be called on all
602
+ ;; objects which have been flagged for special free, so be
603
+ ;; sure to check for the right argument type!
604
+
605
+ (fli:define-foreign-type ws-socket () '(:unsigned :int))
606
+ (fli:define-foreign-type win32-handle () '(:unsigned :int))
607
+ (fli:define-c-struct wsa-network-events (network-events :long)
608
+ (error-code (:c-array :int 10)))
609
+
610
+ (fli:define-foreign-function (wsa-event-create "WSACreateEvent" :source)
611
+ ()
612
+ :lambda-list nil
613
+ :result-type :int
614
+ :module "ws2_32")
615
+
616
+ (fli:define-foreign-function (wsa-event-close "WSACloseEvent" :source)
617
+ ((event-object win32-handle))
618
+ :result-type :int
619
+ :module "ws2_32")
620
+
621
+ (fli:define-foreign-function (wsa-enum-network-events "WSAEnumNetworkEvents" :source)
622
+ ((socket ws-socket)
623
+ (event-object win32-handle)
624
+ (network-events (:reference-return wsa-network-events)))
625
+ :result-type :int
626
+ :module "ws2_32")
627
+
628
+ (fli:define-foreign-function (wsa-event-select "WSAEventSelect" :source)
629
+ ((socket ws-socket)
630
+ (event-object win32-handle)
631
+ (network-events :long))
632
+ :result-type :int
633
+ :module "ws2_32")
634
+
635
+ (fli:define-foreign-function (wsa-get-last-error "WSAGetLastError" :source)
636
+ ()
637
+ :result-type :int
638
+ :module "ws2_32")
639
+
640
+ (fli:define-foreign-function (wsa-ioctlsocket "ioctlsocket" :source)
641
+ ((socket :long) (cmd :long) (argp (:ptr :long)))
642
+ :result-type :int
643
+ :module "ws2_32")
644
+
645
+
646
+ ;; The Windows system
647
+
648
+
649
+ ;; Now that we have access to the system calls, this is the plan:
650
+
651
+ ;; 1. Receive a wait-list with associated sockets to wait for
652
+ ;; 2. Add all those sockets to an event handle
653
+ ;; 3. Listen for an event on that handle (we have a LispWorks system:: internal for that)
654
+ ;; 4. After listening, detect if there are errors
655
+ ;; (this step is different from Unix, where we can have only one error)
656
+ ;; 5. If so, raise one of them
657
+ ;; 6. If not so, return the sockets which have input waiting for them
658
+
659
+
660
+ (defun maybe-wsa-error (rv &optional socket)
661
+ (unless (zerop rv)
662
+ (raise-usock-err (wsa-get-last-error) socket)))
663
+
664
+ (defun bytes-available-for-read (socket)
665
+ (fli:with-dynamic-foreign-objects ((int-ptr :long))
666
+ (let ((rv (wsa-ioctlsocket (os-socket-handle socket) fionread int-ptr)))
667
+ (if (= 0 rv)
668
+ (fli:dereference int-ptr)
669
+ 0))))
670
+
671
+ (defun socket-ready-p (socket)
672
+ (if (typep socket 'stream-usocket)
673
+ (< 0 (bytes-available-for-read socket))
674
+ (%ready-p socket)))
675
+
676
+ (defun waiting-required (sockets)
677
+ (notany #'socket-ready-p sockets))
678
+
679
+ (defun wait-for-input-internal (wait-list &key timeout)
680
+ (when (waiting-required (wait-list-waiters wait-list))
681
+ (system:wait-for-single-object (wait-list-%wait wait-list)
682
+ "Waiting for socket activity" timeout))
683
+ (update-ready-and-state-slots (wait-list-waiters wait-list)))
684
+
685
+ (defun map-network-events (func network-events)
686
+ (let ((event-map (fli:foreign-slot-value network-events 'network-events))
687
+ (error-array (fli:foreign-slot-pointer network-events 'error-code)))
688
+ (unless (zerop event-map)
689
+ (dotimes (i fd-max-events)
690
+ (unless (zerop (ldb (byte 1 i) event-map)) ;;### could be faster with ash and logand?
691
+ (funcall func (fli:foreign-aref error-array i)))))))
692
+
693
+ (defun update-ready-and-state-slots (sockets)
694
+ (dolist (socket sockets)
695
+ (if (or (and (stream-usocket-p socket)
696
+ (listen (socket-stream socket)))
697
+ (%ready-p socket))
698
+ (setf (state socket) :READ)
699
+ (multiple-value-bind
700
+ (rv network-events)
701
+ (wsa-enum-network-events (os-socket-handle socket) 0 t)
702
+ (if (zerop rv)
703
+ (map-network-events #'(lambda (err-code)
704
+ (if (zerop err-code)
705
+ (setf (%ready-p socket) t
706
+ (state socket) :READ)
707
+ (raise-usock-err err-code socket)))
708
+ network-events)
709
+ (maybe-wsa-error rv socket))))))
710
+
711
+
712
+
713
+ ;; The wait-list part
714
+
715
+ (defun free-wait-list (wl)
716
+ (when (wait-list-p wl)
717
+ (unless (null (wait-list-%wait wl))
718
+ (wsa-event-close (wait-list-%wait wl)))))
719
+
720
+ (eval-when (:load-toplevel :execute)
721
+ (hcl:add-special-free-action 'free-wait-list))
722
+
723
+ (defun %setup-wait-list (wait-list)
724
+ (hcl:flag-special-free-action wait-list)
725
+ (setf (wait-list-%wait wait-list) (wsa-event-create)))
726
+
727
+ (defun %add-waiter (wait-list waiter)
728
+ (let ((events (etypecase waiter
729
+ (stream-server-usocket (logior fd-connect fd-accept fd-close))
730
+ (stream-usocket (logior fd-connect fd-read fd-oob fd-close))
731
+ (datagram-usocket (logior fd-read)))))
732
+ (maybe-wsa-error
733
+ (wsa-event-select (os-socket-handle waiter) (wait-list-%wait wait-list) events)
734
+ waiter)))
735
+
736
+ (defun %remove-waiter (wait-list waiter)
737
+ (maybe-wsa-error
738
+ (wsa-event-select (os-socket-handle waiter) (wait-list-%wait wait-list) 0)
739
+ waiter))
740
+
741
+ ) ; end of WIN32-block