chiark / gitweb /
transmits
[chiark-tcl.git] / dgram / dgram.c
1 /*
2  */
3 /*
4  * dgram-socket create <local>                        => <sockid>
5  * dgram-socket close <sockid>
6  * dgram-socket transmit <sockid> <data> <remote>
7  * dgram-socket on-receive <sockid> [<script>]
8  *    calls, effectively,  eval <script> [list <data> <remote-addr> <socket>]
9  *    if script not supplied, cancel
10  */
11
12 #include "tables.h"
13 #include "hbytes.h"
14
15 typedef struct DgramSocket {
16   int fd;
17 } DgramSocket;
18
19 static int n_socks;
20 static DgramSocket *socks;
21
22 static int sockfail(Tcl_Interp *ip, int fd, const char *m) {
23   int e;
24   e= errno;
25   close(fd);
26   return posixerr(ip,e,m);
27 }
28
29 int do_dgram_socket_create(ClientData cd, Tcl_Interp *ip,
30                           SockAddr_Value local, int *sock_r) {
31   int fd, al, r, sock;
32   const struct sockaddr *sa;
33
34   for (sock=0; sock<n_socks && socks[sock].fd>=0; sock++);
35   if (sock>=n_socks) {
36     n_socks += 2;
37     n_socks *= 2;
38     socks= (void*)Tcl_Realloc((void*)socks, n_socks*sizeof(*socks));
39     while (sock<n_socks) socks[sock++].fd=-1;
40     sock--;
41   }
42
43   sa= sockaddr_addr(&local);
44   al= sockaddr_len(&local);
45
46   fd= socket(sa->sa_family, SOCK_DGRAM, 0);
47   if (fd<0) return posixerr(ip,errno,"socket");
48   r= bind(fd, sa, al);  if (r) return sockfail(ip,fd,"bind");
49   r= setnonblock(fd, 1);  if (r) return sockfail(ip,fd,"setnonblock");
50
51   socks[sock].fd= fd;
52   *sock_r= sock;
53   return TCL_OK;
54 }
55
56 int do_dgram_socket_transmit(ClientData cd, Tcl_Interp *ip,
57                              int sock, HBytes_Value data,
58                              SockAddr_Value remote) {
59   int l, r;
60
61   r= sendto(socks[sock].fd,
62             hbytes_data(&data), l=hbytes_len(&data),
63             0,
64             sockaddr_addr(&remote), sockaddr_len(&remote));
65   if (r==-1) return posixerr(ip,errno,"sendto");
66   else if (r!=l) return staticerr(ip,"sendto gave wrong answer");
67   return TCL_OK;
68 }
69   
70 int do_dgram_socket_close(ClientData cd, Tcl_Interp *ip, int sock) {
71   close(socks[sock].fd); /* nothing useful to be done with errors */
72   socks[sock].fd= -1;
73   return TCL_OK;
74 }
75
76 /* Arg parsing */
77
78 int pat_sockid(Tcl_Interp *ip, Tcl_Obj *o, int *val) {
79   int rc, sock;
80   
81   rc= Tcl_ConvertToType(ip,o,&sockid_type);
82   if (rc) return rc;
83
84   sock= o->internalRep.longValue;
85   if (sock >= n_socks || socks[sock].fd==-1)
86     return staticerr(ip,"dgram socket not open");
87
88   *val= sock;
89   return TCL_OK;
90 }
91
92 Tcl_Obj *ret_sockid(Tcl_Interp *ip, int val) {
93   Tcl_Obj *o;
94
95   o= Tcl_NewObj();
96   Tcl_InvalidateStringRep(o);
97   o->internalRep.longValue= val;
98   o->typePtr= &sockid_type;
99   return o;
100 }
101
102 static void sockid_t_free(Tcl_Obj *o) { }
103
104 static void sockid_t_dup(Tcl_Obj *src, Tcl_Obj *dup) {
105   dup->internalRep= src->internalRep;
106 }
107
108 static void sockid_t_ustr(Tcl_Obj *o) {
109   char buf[100];
110   int l;
111
112   snprintf(buf,sizeof(buf),"dgramsock%d", (int)o->internalRep.longValue);
113   l= o->length= strlen(buf);
114   o->bytes= TALLOC(l+1);
115   strcpy(o->bytes, buf);
116 }
117
118 static int sockid_t_sfa(Tcl_Interp *ip, Tcl_Obj *o) {
119   unsigned long ul;
120   char *ep, *str;
121   
122   str= Tcl_GetStringFromObj(o,0);
123   if (memcmp(str,"dgramsock",9)) return staticerr(ip,"bad dgram socket id");
124   errno=0; ul=strtoul(str+9,&ep,10);
125   if (errno || *ep) return staticerr(ip,"bad dgram socket id number");
126   if (ul > INT_MAX) return staticerr(ip,"out of range dgram socket id");
127   o->internalRep.longValue= ul;
128   return TCL_OK;
129 }
130
131 Tcl_ObjType sockid_type = {
132   "sockid-nearly",
133   sockid_t_free, sockid_t_dup, sockid_t_ustr, sockid_t_sfa
134 };