chiark / gitweb /
transmits
[chiark-tcl.git] / dgram / dgram.c
index ecdd2f7355173ca677f860630a12004b4bac6777..7e98df224c4842facb5590cf6da01905693b1a23 100644 (file)
@@ -4,13 +4,21 @@
  * dgram-socket create <local>                        => <sockid>
  * dgram-socket close <sockid>
  * dgram-socket transmit <sockid> <data> <remote>
- * dgram-socket on-receive <sockid> <script>
+ * dgram-socket on-receive <sockid> [<script>]
  *    calls, effectively,  eval <script> [list <data> <remote-addr> <socket>]
+ *    if script not supplied, cancel
  */
 
 #include "tables.h"
 #include "hbytes.h"
 
+typedef struct DgramSocket {
+  int fd;
+} DgramSocket;
+
+static int n_socks;
+static DgramSocket *socks;
+
 static int sockfail(Tcl_Interp *ip, int fd, const char *m) {
   int e;
   e= errno;
@@ -19,10 +27,19 @@ static int sockfail(Tcl_Interp *ip, int fd, const char *m) {
 }
 
 int do_dgram_socket_create(ClientData cd, Tcl_Interp *ip,
-                         SockAddr_Value local, int *result) {
-  int fd, al, r;
+                         SockAddr_Value local, int *sock_r) {
+  int fd, al, r, sock;
   const struct sockaddr *sa;
 
+  for (sock=0; sock<n_socks && socks[sock].fd>=0; sock++);
+  if (sock>=n_socks) {
+    n_socks += 2;
+    n_socks *= 2;
+    socks= (void*)Tcl_Realloc((void*)socks, n_socks*sizeof(*socks));
+    while (sock<n_socks) socks[sock++].fd=-1;
+    sock--;
+  }
+
   sa= sockaddr_addr(&local);
   al= sockaddr_len(&local);
 
@@ -30,19 +47,45 @@ int do_dgram_socket_create(ClientData cd, Tcl_Interp *ip,
   if (fd<0) return posixerr(ip,errno,"socket");
   r= bind(fd, sa, al);  if (r) return sockfail(ip,fd,"bind");
   r= setnonblock(fd, 1);  if (r) return sockfail(ip,fd,"setnonblock");
-  *result= fd;
+
+  socks[sock].fd= fd;
+  *sock_r= sock;
+  return TCL_OK;
+}
+
+int do_dgram_socket_transmit(ClientData cd, Tcl_Interp *ip,
+                            int sock, HBytes_Value data,
+                            SockAddr_Value remote) {
+  int l, r;
+
+  r= sendto(socks[sock].fd,
+           hbytes_data(&data), l=hbytes_len(&data),
+           0,
+           sockaddr_addr(&remote), sockaddr_len(&remote));
+  if (r==-1) return posixerr(ip,errno,"sendto");
+  else if (r!=l) return staticerr(ip,"sendto gave wrong answer");
+  return TCL_OK;
+}
+  
+int do_dgram_socket_close(ClientData cd, Tcl_Interp *ip, int sock) {
+  close(socks[sock].fd); /* nothing useful to be done with errors */
+  socks[sock].fd= -1;
   return TCL_OK;
 }
 
 /* Arg parsing */
 
 int pat_sockid(Tcl_Interp *ip, Tcl_Obj *o, int *val) {
-  int rc;
+  int rc, sock;
   
   rc= Tcl_ConvertToType(ip,o,&sockid_type);
   if (rc) return rc;
 
-  *val= o->internalRep.longValue;
+  sock= o->internalRep.longValue;
+  if (sock >= n_socks || socks[sock].fd==-1)
+    return staticerr(ip,"dgram socket not open");
+
+  *val= sock;
   return TCL_OK;
 }