1###########################################################################
2##
3#W xstream.gi               The SCSCP package             Alexander Konovalov
4#W                                                               Steve Linton
5##
6###########################################################################
7
8
9###########################################################################
10##
11##  InputOutputTCPStream( <hostname>, <port> )
12##  InputOutputTCPStream( <socket_descriptor> )
13##
14##  The first usage is to be used by client and specifies hostname and port.
15##  The second usage is to be used by server and specifies socket descriptor
16##  which will be used to accept incoming connections.
17##
18InstallGlobalFunction( InputOutputTCPStream,
19function( arg )
20# InputOutputLocalProcess has 3 arguments: cdir, exec, argts
21# at least for now, we want to preserve four components of the internal
22# representation of input/output streams, with the following correspondence
23# 1) ptynum --> object in the category IsFile
24# 2) basename --> hostname
25# 3) argts --> ??? arguments to be communicated to the hostname?
26# 4) false --> false # what'is the meaning of this?
27local hostname, port, lookup, sock, res, fio, socket_descriptor, i;
28if Length( arg ) = 2 then # client case
29  hostname := arg[1];
30  port := arg[2];
31  if not IsString( hostname ) then
32    Error( "InputOutputTCPStream: <hostname> must be a string! \n");
33  fi;
34  if not ( IsInt(port) and port >= 0 ) then
35    Error( "InputOutputTCPStream: <port> must be a non-negative integer! \n");
36  fi;
37  # try to lookup the host for up to ten times
38  for i in [1..10] do
39    lookup := IO_gethostbyname( hostname );
40    if lookup <> fail then
41      break;
42    fi;
43  od;
44  if lookup = fail then
45    Print( "InputOutputTCPStream: cannot find hostname ", hostname, "\n");
46    return fail;
47  fi;
48  Info( InfoSCSCP, 1, "Creating a socket ..." );
49  sock := IO_socket( IO.PF_INET, IO.SOCK_STREAM, "tcp" );
50  Info( InfoSCSCP, 1, "Connecting to a remote socket via TCP/IP ..." );
51  res := IO_connect( sock, IO_make_sockaddr_in( lookup.addr[1], port ) );
52  if res = fail then
53    Print( "Error: ", LastSystemError(), "\n" );
54    IO_close( sock );
55    return fail;
56  else
57    fio := IO_WrapFD( sock, IO.DefaultBufSize, IO.DefaultBufSize );
58    return Objectify( InputOutputTCPStreamDefaultType,
59                      [ fio, hostname, [ port ], false ] );
60  fi;
61elif Length( arg ) = 1 then # server case
62  socket_descriptor := arg[1];
63  if not ( IsInt(socket_descriptor) and socket_descriptor >= 0 ) then
64    Error( "InputOutputTCPStream: <socket_descriptor> must be a non-negative integer! \n");
65  fi;
66  fio := IO_WrapFD( socket_descriptor, IO.DefaultBufSize, IO.DefaultBufSize );
67  return Objectify( InputOutputTCPStreamDefaultType,
68                    [ fio, "socket descriptor", [ socket_descriptor ], false ] );
69else
70  Error( "InputOutputTCPStream: usage \n",
71         "InputOutputTCPStream(<hostname>, <port>) for client, \n",
72         "InputOutputTCPStream(<socket_descriptor>) for server! \n");
73fi;
74end);
75
76
77###########################################################################
78##
79#M  ViewObj( <ioTCPstream> )
80##
81InstallMethod( ViewObj, "for ioTCPstream",
82[ IsInputOutputTCPStreamRep and IsInputOutputStream ],
83function(stream)
84    Print("< ");
85    if IsClosedStream(stream) then
86        Print("closed ");
87    fi;
88    Print("input/output TCP stream to ",stream![2],":", stream![3][1], " >");
89end);
90
91
92###########################################################################
93##
94#M  PrintObj( <ioTCPstream> )
95##
96InstallMethod( PrintObj, "for ioTCPstream",
97[ IsInputOutputTCPStreamRep and IsInputOutputStream ],
98function(stream)
99    local i;
100    Print("< ");
101    if IsClosedStream(stream) then
102        Print("closed ");
103    fi;
104    Print("input/output TCP stream to ",stream![2],":", stream![3][1], " >");
105end);
106
107
108###########################################################################
109##
110#M  ReadByte( <ioTCPstream> )
111##
112InstallMethod( ReadByte, "for ioTCPstream",
113[ IsInputOutputTCPStreamRep and IsInputOutputStream ],
114function(stream)
115local buf;
116    buf := IO_Read( stream![1], 1 );
117    if buf = fail or Length(buf) = 0 then
118        stream![4] := true;
119        return fail;
120    else
121        stream![4] := true;
122        return INT_CHAR(buf[1]);
123    fi;
124end);
125
126
127###########################################################################
128##
129#M  ReadLine( <ioTCPstream> )
130##
131InstallMethod( ReadLine, "for ioTCPstream",
132[ IsInputOutputTCPStreamRep and IsInputOutputStream ],
133function( stream )
134    local sofar, chunk;
135    sofar := IO_Read( stream![1], 1 );
136    if sofar = fail or Length(sofar) = 0 then
137        stream![4] := true;
138        return fail;
139    fi;
140    while sofar[ Length(sofar) ] <> '\n' do
141        chunk := IO_Read( stream![1], 1);
142        if chunk = fail or Length(chunk) = 0 then
143            stream![4] := true;
144            return sofar;
145        fi;
146        Append( sofar, chunk );
147    od;
148    return sofar;
149end);
150
151
152###########################################################################
153##
154#M  ReadAllIoTCPStream( <ioTCPstream> )
155##
156BindGlobal( "ReadAllIoTCPStream", function(stream, limit)
157    local sofar, chunk, csize;
158    if limit = -1 then
159        csize := 20000;
160    else
161        csize := Minimum(20000,limit);
162        limit := limit - csize;
163    fi;
164    sofar := IO_Read(stream![1], csize);
165    if sofar = fail or Length(sofar) = 0 then
166        stream![4] := true;
167        return fail;
168    fi;
169    while limit <> 0  do
170        if limit = -1 then
171            csize := 20000;
172        else
173            csize := Minimum(20000,limit);
174            limit := limit - csize;
175        fi;
176        chunk := IO_Read( stream![1], csize);
177        if chunk = fail or Length(chunk) = 0 then
178            stream![4] := true;
179            return sofar;
180        fi;
181        Append(sofar,chunk);
182    od;
183    return sofar;
184end);
185
186
187InstallMethod( ReadAll, "for ioTCPstream",
188    [ IsInputOutputTCPStreamRep and IsInputOutputStream ],
189    stream ->  ReadAllIoTCPStream(stream, -1) );
190
191
192InstallMethod( ReadAll, "for ioTCPstream",
193[ IsInputOutputTCPStreamRep and IsInputOutputStream, IsInt ],
194function( stream, limit )
195    if limit < 0 then
196        Error("ReadAll: negative limit not allowed");
197    fi;
198    return  ReadAllIoTCPStream(stream, limit);
199end);
200
201
202###########################################################################
203##
204#M  WriteByte( <ioTCPstream> )
205##
206InstallMethod( WriteByte, "for ioTCPstream",
207[ IsInputOutputTCPStreamRep and IsInputOutputStream, IsInt ],
208function(stream, byte)
209    local ret,s;
210    if byte < 0 or 255 < byte  then
211        Error( "<byte> must an integer between 0 and 255" );
212    fi;
213    s := [CHAR_INT(byte)];
214    ConvertToStringRep( s );
215    ret := IO_Write( stream![1], s );
216    if ret <> 1 then
217        return fail;
218    else
219        return true;
220    fi;
221end);
222
223
224###########################################################################
225##
226#M  WriteLine( <ioTCPstream>, <string> ) . . . write plus newline and flush
227##
228InstallMethod( WriteLine, "for ioTCPstream",
229[ IsInputOutputTCPStreamRep and IsInputOutputStream, IsString ],
230function( stream, string )
231#    local res;
232#    res := WriteAll( stream, string );
233#    if res <> true  then
234#      return res;
235#    fi;
236#    WriteByte( stream, INT_CHAR('\n') );
237#    return IO_Flush( stream![1] );
238return IO_WriteLine( stream![1], string );
239end );
240
241
242###########################################################################
243##
244#M  WriteAll( <ioTCPstream>, <string> )  . . . . . . . . .  write all bytes
245##
246InstallMethod( WriteAll, "for ioTCPstream",
247[ IsInputOutputTCPStreamRep and IsInputOutputStream, IsString ],
248function( stream, string )
249    local   byte;
250    for byte in string  do
251        if WriteByte( stream, INT_CHAR(byte) ) <> true  then
252            return fail;
253        fi;
254    od;
255    return true;
256end );
257
258
259###########################################################################
260##
261#M IsEndOfStream( <ioTCPstream> )
262##
263InstallMethod( IsEndOfStream, "iostream",
264    [ IsInputOutputTCPStreamRep and IsInputOutputStream ],
265    stream -> not IO_HasData( stream![1] ) );
266    # or IS_BLOCKED_IOSTREAM(stream![1]) );
267
268
269###########################################################################
270##
271#M  CloseStream( <ioTCPstream> )
272##
273InstallMethod( CloseStream, "for ioTCPstream",
274[ IsInputOutputTCPStreamRep and IsInputOutputStream ],
275function(stream)
276    IO_Close( stream![1] );
277    SetFilterObj( stream, IsClosedStream );
278end);
279
280
281###########################################################################
282##
283#M  FileDescriptorOfStream( <ioTCPstream> )
284##
285InstallMethod( FileDescriptorOfStream, "for ioTCPstream",
286[ IsInputOutputTCPStreamRep and IsInputOutputStream ],
287stream -> IO_GetFD( stream![1] ) );
288
289
290###########################################################################
291##
292#F  SCSCPwait( <ioTCPstream>, [ <timeout>] )
293##
294InstallGlobalFunction( SCSCPwait,
295function( arg )
296if Length(arg) = 2 then
297    IO_Select( [ arg[1]![1] ], [ ], [ ], [ ], arg[2], 0 );
298elif Length(arg) = 1 then
299    IO_Select( [ arg[1]![1] ], [ ], [ ], [ ], 3600, 0 );
300fi;
301end);
302
303###########################################################################
304##
305#E
306##
307