1 (* ClientSocket.mod provides a client TCP interface for ChanId's.
3 Copyright (C) 2008-2024 Free Software Foundation, Inc.
4 Contributed by Gaius Mulley <gaius.mulley@southwales.ac.uk>.
6 This file is part of GNU Modula-2.
8 GNU Modula-2 is free software; you can redistribute it and/or modify
9 it under the terms of the GNU General Public License as published by
10 the Free Software Foundation; either version 3, or (at your option)
13 GNU Modula-2 is distributed in the hope that it will be useful, but
14 WITHOUT ANY WARRANTY; without even the implied warranty of
15 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
16 General Public License for more details.
18 Under Section 7 of GPL version 3, you are granted additional
19 permissions described in the GCC Runtime Library Exception, version
20 3.1, as published by the Free Software Foundation.
22 You should have received a copy of the GNU General Public License and
23 a copy of the GCC Runtime Library Exception along with this program;
24 see the files COPYING3 and COPYING.RUNTIME respectively. If not, see
25 <http://www.gnu.org/licenses/>. *)
27 IMPLEMENTATION MODULE ClientSocket
;
30 FROM ASCII
IMPORT nul
, lf
, cr
;
31 FROM ChanConsts
IMPORT ChanFlags
;
32 FROM RTio
IMPORT GetDeviceId
;
33 FROM RTgenif
IMPORT GenDevIF
, InitGenDevIF
;
34 FROM RTdata
IMPORT ModuleId
, MakeModuleId
, InitData
, GetData
, KillData
;
35 FROM IOChan
IMPORT ChanExceptions
, InvalidChan
, CurrentFlags
;
36 FROM IOConsts
IMPORT ReadResults
;
38 FROM IOLink
IMPORT DeviceId
, DeviceTable
, DeviceTablePtr
, DeviceTablePtrValue
, IsDevice
,
39 AllocateDeviceId
, RAISEdevException
, MakeChan
, UnMakeChan
;
41 FROM Storage
IMPORT ALLOCATE
, DEALLOCATE
;
42 FROM Strings
IMPORT Append
;
43 FROM SYSTEM
IMPORT ADDRESS
, ADR
, LOC
;
44 FROM libc
IMPORT read
, write
, close
;
45 FROM errno
IMPORT geterrno
;
46 FROM ErrnoCategory
IMPORT GetOpenResults
;
47 FROM WholeStr
IMPORT IntToStr
;
49 FROM RTgen
IMPORT ChanDev
, DeviceType
, InitChanDev
,
50 doLook
, doSkip
, doSkipLook
, doWriteLn
,
51 doReadText
, doWriteText
, doReadLocs
, doWriteLocs
,
54 FROM wrapsock
IMPORT clientInfo
, clientOpen
, clientOpenIP
, getClientPortNo
,
55 getClientSocketFd
, getClientIP
, getSizeOfClientInfo
,
56 getPushBackChar
, setPushBackChar
, getClientHostname
;
60 PtrToLoc
= POINTER TO LOC
;
61 ClientInfo
= ADDRESS
;
66 ClientInfoSize
: CARDINAL ;
69 PROCEDURE look (d
: DeviceTablePtr
;
70 VAR ch
: CHAR; VAR r
: ReadResults
) ;
76 PROCEDURE skip (d
: DeviceTablePtr
) ;
82 PROCEDURE skiplook (d
: DeviceTablePtr
;
83 VAR ch
: CHAR; VAR r
: ReadResults
) ;
85 doSkipLook(dev
, d
, ch
, r
)
89 PROCEDURE lnwrite (d
: DeviceTablePtr
) ;
95 PROCEDURE textread (d
: DeviceTablePtr
;
98 VAR charsRead
: CARDINAL) ;
100 doReadText(dev
, d
, to
, maxChars
, charsRead
)
104 PROCEDURE textwrite (d
: DeviceTablePtr
;
106 charsToWrite
: CARDINAL);
108 doWriteText(dev
, d
, from
, charsToWrite
)
112 PROCEDURE rawread (d
: DeviceTablePtr
;
115 VAR locsRead
: CARDINAL) ;
117 doReadLocs(dev
, d
, to
, maxLocs
, locsRead
)
121 PROCEDURE rawwrite (d
: DeviceTablePtr
;
123 locsToWrite
: CARDINAL) ;
125 doWriteLocs(dev
, d
, from
, locsToWrite
)
130 doreadchar - returns a CHAR from the file associated with, g.
133 PROCEDURE doreadchar (g
: GenDevIF
; d
: DeviceTablePtr
) : CHAR ;
140 c
:= GetData(d
, mid
) ;
142 fd
:= getClientSocketFd(c
) ;
143 IF NOT getPushBackChar(c
, ch
)
146 i
:= read(fd
, ADR(ch
), SIZE(ch
))
159 dounreadchar - pushes a CHAR back onto the file associated with, g.
162 PROCEDURE dounreadchar (g
: GenDevIF
; d
: DeviceTablePtr
; ch
: CHAR) : CHAR ;
167 c
:= GetData(d
, mid
) ;
169 fd
:= getClientSocketFd(c
) ;
170 IF NOT setPushBackChar(c
, ch
)
172 RAISEdevException(cid
, did
, notAvailable
,
173 'ClientSocket.dounreadchar: number of characters pushed back exceeds buffer')
181 dogeterrno - returns the errno relating to the generic device.
184 PROCEDURE dogeterrno (g
: GenDevIF
; d
: DeviceTablePtr
) : INTEGER ;
191 dorbytes - reads upto, max, bytes setting, actual, and
192 returning FALSE if an error (not due to eof)
196 PROCEDURE dorbytes (g
: GenDevIF
; d
: DeviceTablePtr
;
199 VAR actual
: CARDINAL) : BOOLEAN ;
206 c
:= GetData(d
, mid
) ;
211 IF getPushBackChar(c
, p^
)
216 fd
:= getClientSocketFd(c
) ;
217 i
:= read(fd
, p
, max
) ;
223 errNum
:= geterrno() ;
235 dowbytes - attempts to write out nBytes. The actual
236 number of bytes written are returned.
237 If the actual number of bytes written is >= 0 then
238 the return result will be true. Failure to
239 write any bytes results in returning FALSE
240 errno set and the actual will be set to zero.
243 PROCEDURE dowbytes (g
: GenDevIF
; d
: DeviceTablePtr
;
246 VAR actual
: CARDINAL) : BOOLEAN ;
252 c
:= GetData(d
, mid
) ;
254 fd
:= getClientSocketFd(c
) ;
255 i
:= write(fd
, from
, nBytes
) ;
261 errNum
:= geterrno() ;
270 dowriteln - attempt to write an end of line marker to the
271 file and returns TRUE if successful.
274 PROCEDURE dowriteln (g
: GenDevIF
; d
: DeviceTablePtr
) : BOOLEAN ;
276 a
: ARRAY [0.
.1] OF CHAR ;
281 RETURN( dowbytes(g
, d
, ADR(a
), SIZE(a
), i
) AND (i
=SIZE(a
)) )
286 iseof - returns TRUE if end of file is seen.
289 PROCEDURE iseof (g
: GenDevIF
; d
: DeviceTablePtr
) : BOOLEAN ;
293 ch
:= doreadchar(g
, d
) ;
297 ch
:= dounreadchar(g
, d
, ch
) ;
307 iseoln - returns TRUE if end of line is seen.
310 PROCEDURE iseoln (g
: GenDevIF
; d
: DeviceTablePtr
) : BOOLEAN ;
314 ch
:= doreadchar(g
, d
) ;
318 ch
:= dounreadchar(g
, d
, ch
) ;
328 iserror - returns TRUE if an error was seen on the device.
331 PROCEDURE iserror (g
: GenDevIF
; d
: DeviceTablePtr
) : BOOLEAN ;
333 RETURN( d^.errNum#
0 )
337 PROCEDURE getname (d
: DeviceTablePtr
;
338 VAR a
: ARRAY OF CHAR) ;
341 b
: ARRAY [0.
.6] OF CHAR ;
343 c
:= GetData(d
, mid
) ;
344 getClientHostname(c
, ADR(a
), HIGH(a
)) ;
346 IntToStr(getClientPortNo(c
) , b
) ;
352 freeData - disposes of, c.
355 PROCEDURE freeData (c
: ClientInfo
) ;
357 DEALLOCATE(c
, ClientInfoSize
) ;
365 PROCEDURE handlefree (d
: DeviceTablePtr
) ;
371 c
:= GetData(d
, mid
) ;
372 fd
:= getClientSocketFd(c
) ;
380 OpenSocket - opens a TCP client connection to host:port.
383 PROCEDURE OpenSocket (VAR cid
: ChanId
;
384 host
: ARRAY OF CHAR; port
: CARDINAL;
385 f
: FlagSet
; VAR res
: OpenResults
) ;
391 MakeChan(did
, cid
) ; (* create new channel *)
392 ALLOCATE(c
, ClientInfoSize
) ; (* allocate client socket memory *)
393 d
:= DeviceTablePtrValue(cid
, did
) ;
394 InitData(d
, mid
, c
, freeData
) ; (* attach memory to device and module *)
395 res
:= clientOpen(c
, ADR(host
), LENGTH(host
), port
) ;
407 doSkipLook
:= skiplook
;
408 doLnWrite
:= lnwrite
;
409 doTextRead
:= textread
;
410 doTextWrite
:= textwrite
;
411 doRawRead
:= rawread
;
412 doRawWrite
:= rawwrite
;
413 doGetName
:= getname
;
420 IsSocket - tests if the channel identified by cid is open as
421 a client socket stream.
424 PROCEDURE IsSocket (cid
: ChanId
) : BOOLEAN ;
426 RETURN( (cid #
NIL) AND (InvalidChan() # cid
) AND
427 (IsDevice(cid
, did
)) AND
428 ((readFlag
IN CurrentFlags(cid
)) OR
429 (writeFlag
IN CurrentFlags(cid
))) )
434 Close - if the channel identified by cid is not open to a socket
435 stream, the exception wrongDevice is raised; otherwise
436 closes the channel, and assigns the value identifying
437 the invalid channel to cid.
440 PROCEDURE Close (VAR cid
: ChanId
) ;
444 UnMakeChan(did
, cid
) ;
447 RAISEdevException(cid
, did
, wrongDevice
,
448 'ClientSocket.' + __FUNCTION__
+
449 ': channel is not a socket stream')
463 ClientInfoSize
:= getSizeOfClientInfo() ;
464 AllocateDeviceId(did
) ;
465 gen
:= InitGenDevIF(did
, doreadchar
, dounreadchar
,
466 dogeterrno
, dorbytes
, dowbytes
,
468 iseof
, iseoln
, iserror
) ;
469 dev
:= InitChanDev(streamfile
, did
, gen
)