aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rwxr-xr-xconfigure.ac8
-rw-r--r--examples/sqlite3.tcl8
-rw-r--r--examples/udp.client18
-rw-r--r--examples/udp.server2
-rw-r--r--examples/udp6.client27
-rw-r--r--examples/udp6.server26
-rw-r--r--jim-aio.c317
-rw-r--r--jim.h1
-rw-r--r--jim_tcl.txt47
9 files changed, 365 insertions, 89 deletions
diff --git a/configure.ac b/configure.ac
index ddc9e3a..f53455f 100755
--- a/configure.ac
+++ b/configure.ac
@@ -40,6 +40,14 @@ AC_ARG_ENABLE(math,
fi
]
)
+AC_ARG_ENABLE(ipv6,
+ [ --enable-ipv6 include ipv6 support in the aio extension],
+ [
+ if test "x$enableval" = "xyes" ; then
+ EXTRA_CFLAGS="$EXTRA_CFLAGS -DJIM_IPV6"
+ fi
+ ]
+)
jim_extensions="stdlib load package readdir glob array clock exec file posix regexp signal tclcompat aio bio eventloop syslog"
AC_ARG_WITH(jim-ext,
diff --git a/examples/sqlite3.tcl b/examples/sqlite3.tcl
new file mode 100644
index 0000000..9365cdf
--- /dev/null
+++ b/examples/sqlite3.tcl
@@ -0,0 +1,8 @@
+set db [sqlite3.open :memory:]
+$db query {CREATE TABLE plays (id, author, title)}
+$db query {INSERT INTO plays (id, author, title) VALUES (1, 'Goethe', 'Faust');}
+$db query {INSERT INTO plays (id, author, title) VALUES (2, 'Shakespeare', 'Hamlet');}
+$db query {INSERT INTO plays (id, author, title) VALUES (3, 'Sophocles', 'Oedipus Rex');}
+set res [$db query "SELECT * FROM plays"]
+$db close
+foreach r $res {puts $r(author)}
diff --git a/examples/udp.client b/examples/udp.client
index 32dbc02..da74e77 100644
--- a/examples/udp.client
+++ b/examples/udp.client
@@ -2,10 +2,26 @@
set s [socket dgram]
-foreach i [range 1 20] {
+foreach i [range 1 5] {
# Specify the address and port with sendto
$s sendto "$i + $i + 10" 127.0.0.1:20000
# Receive the response - max length of 100
puts [$s recvfrom 100]
}
+
+$s close
+
+# Now sending via a connected udp socket
+
+set s [socket dgram 127.0.0.1:20000]
+
+foreach i [range 5 10] {
+ # Socket is connected, so can just use puts here
+ # But remember to flush to ensure that each message is separate
+ $s puts -nonewline "$i * $i"
+ $s flush
+
+ # Receive the response - max length of 100
+ puts [$s recvfrom 100]
+}
diff --git a/examples/udp.server b/examples/udp.server
index c462647..03f41cd 100644
--- a/examples/udp.server
+++ b/examples/udp.server
@@ -16,7 +16,7 @@ $s readable {
set result "Error: $buf => $msg"
}
- puts ", sending '$result'"
+ puts ", sending '$result' to $addr"
# Send the result back to where it came from
$s sendto $result $addr
diff --git a/examples/udp6.client b/examples/udp6.client
new file mode 100644
index 0000000..f3e680e
--- /dev/null
+++ b/examples/udp6.client
@@ -0,0 +1,27 @@
+# Example of sending from an unconnected ipv6 socket
+
+set s [socket -ipv6 dgram]
+
+foreach i [range 1 5] {
+ # Specify the address and port with sendto
+ $s sendto "$i + $i + 10" {[::1]:20000}
+
+ # Receive the response - max length of 100
+ puts [$s recvfrom 100]
+}
+
+$s close
+
+# Now sending via a connected udp socket
+
+set s [socket -ipv6 dgram {[::1]:20000}]
+
+foreach i [range 5 10] {
+ # Socket is connected, so can just use puts here
+ # But remember to flush to ensure that each message is separate
+ $s puts -nonewline "$i * $i"
+ $s flush
+
+ # Receive the response - max length of 100
+ puts [$s recvfrom 100]
+}
diff --git a/examples/udp6.server b/examples/udp6.server
new file mode 100644
index 0000000..da9b4bf
--- /dev/null
+++ b/examples/udp6.server
@@ -0,0 +1,26 @@
+# Example of a udp server listening on ipv6 which sends a response
+# Note that on many hosts, this will also respond to ipv4 requests too
+
+# Listen on port 20000.
+set s [socket -ipv6 dgram.server {[::]:20000}]
+
+# For each request...
+$s readable {
+ # Get the request (max 80 chars) - need the source address
+ set buf [$s recvfrom 80 addr]
+
+ puts -nonewline "read '$buf' from $addr"
+
+ try {
+ set result "$buf = [expr $buf]"
+ } on error {msg} {
+ set result "Error: $buf => $msg"
+ }
+
+ puts ", sending '$result'"
+
+ # Send the result back to where it came from
+ $s sendto $result $addr
+}
+
+vwait done
diff --git a/jim-aio.c b/jim-aio.c
index 52dfa8d..93ec8a3 100644
--- a/jim-aio.c
+++ b/jim-aio.c
@@ -65,6 +65,24 @@
#define AIO_KEEPOPEN 1
+#if defined(JIM_IPV6)
+#define IPV6 1
+#else
+#define IPV6 0
+#ifndef AF_INET6
+#define AF_INET6 0
+#endif
+#endif
+
+#ifndef JIM_ANSIC
+union sockaddr_any {
+ struct sockaddr sa;
+ struct sockaddr_in sin;
+#if IPV6
+ struct sockaddr_in6 sin6;
+#endif
+};
+#endif
typedef struct AioFile
{
@@ -80,51 +98,124 @@ typedef struct AioFile
Jim_Obj *wEvent;
Jim_Obj *eEvent;
#ifndef JIM_ANSIC
- struct sockaddr sa;
- struct hostent *he;
+ int addr_family;
#endif
} AioFile;
static int JimAioSubCmdProc(Jim_Interp *interp, int argc, Jim_Obj *const *argv);
#ifndef JIM_ANSIC
-static int JimParseIpAddress(Jim_Interp *interp, const char *hostport, struct sockaddr_in *sa)
+static int JimParseIPv6Address(Jim_Interp *interp, const char *hostport, union sockaddr_any *sa, int *salen)
{
- char a[0x20];
- char b[0x20];
- const char *sthost;
- const char *stport;
- unsigned port;
- struct hostent *he;
-
- switch (sscanf(hostport, "%[^:]:%[^:]", a, b)) {
- case 2:
- sthost = a;
- stport = b;
- break;
- case 1:
- sthost = "0.0.0.0";
- stport = a;
- break;
- default:
- return JIM_ERR;
+#if IPV6
+ /*
+ * An IPv6 addr/port looks like:
+ * [::1]
+ * [::1]:2000
+ * [fe80::223:6cff:fe95:bdc0%en1]:2000
+ * [::]:2000
+ * 2000
+ *
+ * Note that the "any" address is ::, which is the same as when no address is specified.
+ */
+ char *sthost = NULL;
+ const char *stport;
+ int ret = JIM_OK;
+ struct addrinfo req;
+ struct addrinfo *ai;
+
+ stport = strrchr(hostport, ':');
+ if (!stport) {
+ /* No : so, the whole thing is the port */
+ stport = hostport;
+ hostport = "::";
+ sthost = Jim_StrDup(hostport);
}
- if (0 == strncmp(sthost, "ANY", 3)) {
- sthost = "0.0.0.0";
+ else {
+ stport++;
}
- port = atol(stport);
- he = gethostbyname(sthost);
- if (!he) {
- Jim_SetResultString(interp, hstrerror(h_errno), -1);
- return JIM_ERR;
+ if (*hostport == '[') {
+ /* This is a numeric ipv6 address */
+ char *pt = strchr(++hostport, ']');
+ if (pt) {
+ sthost = Jim_StrDupLen(hostport, pt - hostport);
+ }
}
- sa->sin_family = he->h_addrtype;
- memcpy(&sa->sin_addr, he->h_addr, he->h_length); /* set address */
- sa->sin_port = htons(port);
+ if (!sthost) {
+ sthost = Jim_StrDupLen(hostport, stport - hostport - 1);
+ }
- return JIM_OK;
+ memset(&req, '\0', sizeof(req));
+ req.ai_family = PF_INET6;
+
+ if (getaddrinfo(sthost, NULL, &req, &ai)) {
+ Jim_SetResultFormatted(interp, "Not a valid address: %s", hostport);
+ ret = JIM_ERR;
+ }
+ else {
+ memcpy(&sa->sin, ai->ai_addr, ai->ai_addrlen);
+ *salen = ai->ai_addrlen;
+
+ sa->sin.sin_port = htons(atoi(stport));
+
+ freeaddrinfo(ai);
+ }
+ Jim_Free(sthost);
+
+ return ret;
+#else
+ Jim_SetResultString(interp, "ipv6 not supported", -1);
+ return JIM_ERR;
+#endif
+}
+
+static int JimParseIpAddress(Jim_Interp *interp, const char *hostport, union sockaddr_any *sa, int *salen)
+{
+ /* An IPv4 addr/port looks like:
+ * 192.168.1.5
+ * 192.168.1.5:2000
+ * 2000
+ *
+ * If the address is missing, INADDR_ANY is used.
+ * If the port is missing, 0 is used (only useful for server sockets).
+ */
+ char *sthost = NULL;
+ const char *stport;
+ int ret = JIM_OK;
+ struct addrinfo req;
+ struct addrinfo *ai;
+
+ stport = strrchr(hostport, ':');
+ if (!stport) {
+ /* No : so, the whole thing is the port */
+ stport = hostport;
+ sthost = Jim_StrDup("0.0.0.0");
+ }
+ else {
+ sthost = Jim_StrDupLen(hostport, stport - hostport);
+ stport++;
+ }
+
+ memset(&req, '\0', sizeof(req));
+ req.ai_family = PF_INET;
+
+ if (getaddrinfo(sthost, NULL, &req, &ai)) {
+ Jim_SetResultFormatted(interp, "Not a valid address: %s", hostport);
+ ret = JIM_ERR;
+ }
+ else {
+ memcpy(&sa->sin, ai->ai_addr, ai->ai_addrlen);
+ *salen = ai->ai_addrlen;
+
+ sa->sin.sin_port = htons(atoi(stport));
+
+ freeaddrinfo(ai);
+ }
+ Jim_Free(sthost);
+
+ return ret;
}
static int JimParseDomainAddress(Jim_Interp *interp, const char *path, struct sockaddr_un *sa)
@@ -333,7 +424,7 @@ static int aio_cmd_recvfrom(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
{
AioFile *af = Jim_CmdPrivData(interp);
char *buf;
- struct sockaddr_in sa;
+ union sockaddr_any sa;
long len;
socklen_t salen = sizeof(sa);
int rlen;
@@ -344,8 +435,9 @@ static int aio_cmd_recvfrom(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
buf = Jim_Alloc(len + 1);
- rlen = recvfrom(fileno(af->fp), buf, len, 0, (struct sockaddr *)&sa, &salen);
+ rlen = recvfrom(fileno(af->fp), buf, len, 0, &sa.sa, &salen);
if (rlen < 0) {
+ perror("recvfrom");
Jim_Free(buf);
JimAioSetError(interp, NULL);
return JIM_ERR;
@@ -353,12 +445,24 @@ static int aio_cmd_recvfrom(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
Jim_SetResult(interp, Jim_NewStringObjNoAlloc(interp, buf, rlen));
if (argc > 1) {
- char buf[50];
-
- inet_ntop(sa.sin_family, &sa.sin_addr, buf, sizeof(buf) - 7);
- snprintf(buf + strlen(buf), 7, ":%d", ntohs(sa.sin_port));
+ char addrbuf[100];
+
+#if IPV6
+ if (sa.sa.sa_family == AF_INET6) {
+ addrbuf[0] = '[';
+ /* Allow 9 for []:65535\0 */
+ inet_ntop(sa.sa.sa_family, &sa.sin6.sin6_addr, addrbuf + 1, sizeof(addrbuf) - 9);
+ snprintf(addrbuf + strlen(addrbuf), 8, "]:%d", ntohs(sa.sin.sin_port));
+ }
+ else
+#endif
+ {
+ /* Allow 7 for :65535\0 */
+ inet_ntop(sa.sa.sa_family, &sa.sin.sin_addr, addrbuf, sizeof(addrbuf) - 7);
+ snprintf(addrbuf + strlen(addrbuf), 7, ":%d", ntohs(sa.sin.sin_port));
+ }
- if (Jim_SetVariable(interp, argv[1], Jim_NewStringObj(interp, buf, -1)) != JIM_OK) {
+ if (Jim_SetVariable(interp, argv[1], Jim_NewStringObj(interp, addrbuf, -1)) != JIM_OK) {
return JIM_ERR;
}
}
@@ -373,16 +477,24 @@ static int aio_cmd_sendto(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
int wlen;
int len;
const char *wdata;
- struct sockaddr_in sa;
+ union sockaddr_any sa;
+ const char *addr = Jim_GetString(argv[1], NULL);
+ int salen;
- if (JimParseIpAddress(interp, Jim_GetString(argv[1], NULL), &sa) != JIM_OK) {
+ if (IPV6 && af->addr_family == AF_INET6) {
+ if (JimParseIPv6Address(interp, addr, &sa, &salen) != JIM_OK) {
+ return JIM_ERR;
+ }
+ }
+ else if (JimParseIpAddress(interp, addr, &sa, &salen) != JIM_OK) {
return JIM_ERR;
}
wdata = Jim_GetString(argv[0], &wlen);
/* Note that we don't validate the socket type. Rely on sendto() failing if appropriate */
- len = sendto(fileno(af->fp), wdata, wlen, 0, (struct sockaddr *)&sa, sizeof(sa));
+ len = sendto(fileno(af->fp), wdata, wlen, 0, &sa.sa, salen);
if (len < 0) {
+ perror("sendto");
JimAioSetError(interp, NULL);
return JIM_ERR;
}
@@ -394,12 +506,13 @@ static int aio_cmd_accept(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
{
AioFile *serv_af = Jim_CmdPrivData(interp);
int sock;
- socklen_t addrlen = sizeof(struct sockaddr_in);
+ union sockaddr_any sa;
+ socklen_t addrlen = sizeof(sa);
AioFile *af;
char buf[AIO_CMD_LEN];
long fileId;
- sock = accept(serv_af->fd, (struct sockaddr *)&serv_af->sa, &addrlen);
+ sock = accept(serv_af->fd, &sa.sa, &addrlen);
if (sock < 0)
return JIM_ERR;
@@ -419,6 +532,7 @@ static int aio_cmd_accept(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
af->rEvent = NULL;
af->wEvent = NULL;
af->eEvent = NULL;
+ af->addr_family = serv_af->addr_family;
sprintf(buf, "aio.sockstream%ld", fileId);
Jim_CreateCommand(interp, buf, JimAioSubCmdProc, af, JimAioDelProc);
Jim_SetResultString(interp, buf, -1);
@@ -796,7 +910,7 @@ static int JimAioOpenCommand(Jim_Interp *interp, int argc,
* Creates the command and lappends the name of the command to the current result.
*
*/
-static int JimMakeChannel(Jim_Interp *interp, Jim_Obj *filename, const char *hdlfmt, int fd,
+static int JimMakeChannel(Jim_Interp *interp, Jim_Obj *filename, const char *hdlfmt, int fd, int family,
const char *mode)
{
long fileId;
@@ -827,6 +941,7 @@ static int JimMakeChannel(Jim_Interp *interp, Jim_Obj *filename, const char *hdl
af->rEvent = NULL;
af->wEvent = NULL;
af->eEvent = NULL;
+ af->addr_family = family;
sprintf(buf, hdlfmt, fileId);
Jim_CreateCommand(interp, buf, JimAioSubCmdProc, af, JimAioDelProc);
@@ -851,12 +966,16 @@ static int JimAioSockCommand(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
enum
{
SOCK_UNIX,
- SOCK_UNIX_SERV,
- SOCK_DGRAM_CL,
- SOCK_DGRAM_SERV,
- SOCK_STREAM_CL,
- SOCK_STREAM_SERV,
+ SOCK_UNIX_SERVER,
+ SOCK_DGRAM_CLIENT,
+ SOCK_DGRAM_SERVER,
+ SOCK_STREAM_CLIENT,
+ SOCK_STREAM_SERVER,
SOCK_STREAM_PIPE,
+ SOCK_DGRAM6_CLIENT,
+ SOCK_DGRAM6_SERVER,
+ SOCK_STREAM6_CLIENT,
+ SOCK_STREAM6_SERVER,
};
int socktype;
int sock;
@@ -864,32 +983,39 @@ static int JimAioSockCommand(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
int res;
int on = 1;
const char *mode = "r+";
+ int family = AF_INET;
+ Jim_Obj *argv0 = argv[0];
+ int ipv6 = 0;
+
+ if (argc > 1 && Jim_CompareStringImmediate(interp, argv[1], "-ipv6")) {
+ if (!IPV6) {
+ Jim_SetResultString(interp, "ipv6 not supported", -1);
+ return JIM_ERR;
+ }
+ ipv6 = 1;
+ family = AF_INET6;
+ }
+ argc -= ipv6;
+ argv += ipv6;
if (argc < 2) {
wrongargs:
- Jim_WrongNumArgs(interp, 1, argv, "type ?address?");
+ Jim_WrongNumArgs(interp, 1, &argv0, "?-ipv6? type ?address?");
return JIM_ERR;
}
if (Jim_GetEnum(interp, argv[1], socktypes, &socktype, "socket type", JIM_ERRMSG) != JIM_OK)
return JIM_ERR;
- if (argc == 3) {
- hostportarg = Jim_GetString(argv[2], NULL);
- }
- else if (argc != 2 || (socktype != SOCK_STREAM_PIPE && socktype != SOCK_DGRAM_CL)) {
- goto wrongargs;
- }
-
Jim_SetResultString(interp, "", 0);
hdlfmt = "aio.sock%ld";
switch (socktype) {
- case SOCK_DGRAM_CL:
+ case SOCK_DGRAM_CLIENT:
if (argc == 2) {
/* No address, so an unconnected dgram socket */
- sock = socket(PF_INET, SOCK_DGRAM, 0);
+ sock = socket(family, SOCK_DGRAM, 0);
if (sock < 0) {
JimAioSetError(interp, NULL);
return JIM_ERR;
@@ -897,20 +1023,33 @@ static int JimAioSockCommand(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
break;
}
/* fall through */
- case SOCK_STREAM_CL:
+ case SOCK_STREAM_CLIENT:
{
- struct sockaddr_in sa;
+ union sockaddr_any sa;
+ int salen;
+
+ if (argc != 3) {
+ goto wrongargs;
+ }
- if (JimParseIpAddress(interp, hostportarg, &sa) != JIM_OK) {
+ hostportarg = Jim_GetString(argv[2], NULL);
+
+ if (ipv6) {
+ if (JimParseIPv6Address(interp, hostportarg, &sa, &salen) != JIM_OK) {
+ return JIM_ERR;
+ }
+ }
+ else if (JimParseIpAddress(interp, hostportarg, &sa, &salen) != JIM_OK) {
return JIM_ERR;
}
- sock = socket(PF_INET, socktype == SOCK_DGRAM_CL ? SOCK_DGRAM : SOCK_STREAM, 0);
+ sock = socket(family, (socktype == SOCK_DGRAM_CLIENT) ? SOCK_DGRAM : SOCK_STREAM, 0);
if (sock < 0) {
JimAioSetError(interp, NULL);
return JIM_ERR;
}
- res = connect(sock, (struct sockaddr *)&sa, sizeof(sa));
+ res = connect(sock, &sa.sa, salen);
if (res) {
+ perror("connect");
JimAioSetError(interp, argv[2]);
close(sock);
return JIM_ERR;
@@ -918,16 +1057,27 @@ static int JimAioSockCommand(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
}
break;
- case SOCK_STREAM_SERV:
- case SOCK_DGRAM_SERV:
+ case SOCK_STREAM_SERVER:
+ case SOCK_DGRAM_SERVER:
{
- struct sockaddr_in sa;
+ union sockaddr_any sa;
+ int salen;
- if (JimParseIpAddress(interp, hostportarg, &sa) != JIM_OK) {
- JimAioSetError(interp, argv[2]);
+ if (argc != 3) {
+ goto wrongargs;
+ }
+
+ hostportarg = Jim_GetString(argv[2], NULL);
+
+ if (ipv6) {
+ if (JimParseIPv6Address(interp, hostportarg, &sa, &salen) != JIM_OK) {
+ return JIM_ERR;
+ }
+ }
+ else if (JimParseIpAddress(interp, hostportarg, &sa, &salen) != JIM_OK) {
return JIM_ERR;
}
- sock = socket(PF_INET, socktype == SOCK_DGRAM_SERV ? SOCK_DGRAM : SOCK_STREAM, 0);
+ sock = socket(family, (socktype == SOCK_DGRAM_SERVER) ? SOCK_DGRAM : SOCK_STREAM, 0);
if (sock < 0) {
JimAioSetError(interp, NULL);
return JIM_ERR;
@@ -936,13 +1086,13 @@ static int JimAioSockCommand(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
/* Enable address reuse */
setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, &on, sizeof(on));
- res = bind(sock, (struct sockaddr *)&sa, sizeof(sa));
+ res = bind(sock, &sa.sa, salen);
if (res) {
JimAioSetError(interp, argv[2]);
close(sock);
return JIM_ERR;
}
- if (socktype != SOCK_DGRAM_SERV) {
+ if (socktype == SOCK_STREAM_SERVER) {
res = listen(sock, 5);
if (res) {
JimAioSetError(interp, NULL);
@@ -959,10 +1109,15 @@ static int JimAioSockCommand(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
struct sockaddr_un sa;
socklen_t len;
+ if (argc != 3 || ipv6) {
+ goto wrongargs;
+ }
+
if (JimParseDomainAddress(interp, hostportarg, &sa) != JIM_OK) {
JimAioSetError(interp, argv[2]);
return JIM_ERR;
}
+ family = PF_UNIX;
sock = socket(PF_UNIX, SOCK_STREAM, 0);
if (sock < 0) {
JimAioSetError(interp, NULL);
@@ -979,15 +1134,20 @@ static int JimAioSockCommand(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
break;
}
- case SOCK_UNIX_SERV:
+ case SOCK_UNIX_SERVER:
{
struct sockaddr_un sa;
socklen_t len;
+ if (argc != 3 || ipv6) {
+ goto wrongargs;
+ }
+
if (JimParseDomainAddress(interp, hostportarg, &sa) != JIM_OK) {
JimAioSetError(interp, argv[2]);
return JIM_ERR;
}
+ family = PF_UNIX;
sock = socket(PF_UNIX, SOCK_STREAM, 0);
if (sock < 0) {
JimAioSetError(interp, NULL);
@@ -1009,17 +1169,22 @@ static int JimAioSockCommand(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
hdlfmt = "aio.sockunixsrv%ld";
break;
}
+
case SOCK_STREAM_PIPE:
{
int p[2];
+ if (argc != 2 || ipv6) {
+ goto wrongargs;
+ }
+
if (pipe(p) < 0) {
JimAioSetError(interp, NULL);
return JIM_ERR;
}
hdlfmt = "aio.pipe%ld";
- if (JimMakeChannel(interp, argv[1], hdlfmt, p[0], "r") != JIM_OK) {
+ if (JimMakeChannel(interp, argv[1], hdlfmt, p[0], family, "r") != JIM_OK) {
close(p[0]);
close(p[1]);
JimAioSetError(interp, NULL);
@@ -1036,7 +1201,7 @@ static int JimAioSockCommand(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
return JIM_ERR;
}
- return JimMakeChannel(interp, argv[1], hdlfmt, sock, mode);
+ return JimMakeChannel(interp, argv[1], hdlfmt, sock, family, mode);
}
#endif
diff --git a/jim.h b/jim.h
index 7a78687..1c0c7a1 100644
--- a/jim.h
+++ b/jim.h
@@ -612,6 +612,7 @@ typedef struct Jim_Reference {
JIM_EXPORT void * Jim_Alloc (int size);
JIM_EXPORT void Jim_Free (void *ptr);
JIM_EXPORT char * Jim_StrDup (const char *s);
+JIM_EXPORT char *Jim_StrDupLen(const char *s, int l);
/* evaluation */
JIM_EXPORT int Jim_Eval(Jim_Interp *interp, const char *script);
diff --git a/jim_tcl.txt b/jim_tcl.txt
index 1b719a8..d2640f8 100644
--- a/jim_tcl.txt
+++ b/jim_tcl.txt
@@ -74,6 +74,7 @@ Since v0.62:
11. Add 'socket pipe'
12. Enhance 'try ... on ... finally' to be more Tcl 8.6 compatible
13. It is now possible to 'return' from within 'try'
+14. IPv6 support is now included
Since v0.61:
@@ -3959,7 +3960,7 @@ aio
Receives a message from the handle via recvfrom(2) and returns it.
At most *maxlen* bytes are read.
If *addrvar* is specified, the sending address of the message is stored in
- the named variable in the form 'hostname:port'.
+ the named variable in the form 'addr:port'. See 'socket' for details.
eventloop: after, vwait
~~~~~~~~~~~~~~~~~~~~~~~
@@ -3978,15 +3979,27 @@ The following commands allow a script to be invoked when the given condition occ
Time-based execution is also available via the eventloop API.
+*after* 'time script'+::
- The script is executed after the given number of milliseconds have elapsed.
- Returns an event id.
+ The script is executed after the given number of milliseconds have elapsed.
+ Returns an event id.
+*after cancel* 'id'+::
- Cancels an after event with the given event id.
+ Cancels an after event with the given event id.
+*vwait* 'variable'+::
- A call to vwait is required to enter the eventloop. 'vwait' processes events until
- the named variabled changes. The variable need not exist beforehand.
+ A call to vwait is required to enter the eventloop. 'vwait' processes events until
+ the named (global) variable changes. The variable need not exist beforehand.
+ If there are no event handlers defined, 'vwait' returns immediately.
+
+Scripts are executed at the global scope. If an error occurs during a handler script,
+an attempt is made to call (the user-defined command) 'bgerror' with the details of the error.
+If the 'bgerror' commands does not exist, it is printed to stderr instead.
+
+If a file event handler script generates an error, the handler is automatically removed
+to prevent infinite errors. (A time event handler is always removed after execution).
+
++*bgerror* 'error'+::
+ Called when an event handler script generates an error.
+
socket
~~~~~~
@@ -3998,18 +4011,18 @@ Various socket types may be created.
+*socket unix.server* 'path'+::
A unix domain socket server.
-+*socket stream* 'hostname:port'+::
++*socket ?-ipv6? stream* 'addr:port'+::
A TCP socket client.
-+*socket stream.server* '?hostname:?port'+::
- A TCP socket server (*hostname* defaults to 0.0.0.0).
++*socket ?-ipv6? stream.server '?addr:?port'+::
+ A TCP socket server (*addr* defaults to 0.0.0.0 for IPv4 or [::] for IPv6).
-+*socket dgram* ?'hostname:port'?+::
++*socket ?-ipv6? dgram* ?'addr:port'?+::
A UDP socket client. If the address is not specified,
the client socket will be unbound and 'sendto' must be used
to indicated the destination.
-+*socket dgram.server* 'hostname:port'+::
++*socket ?-ipv6? dgram.server* 'addr:port'+::
A UDP socket server.
+*socket pipe*+::
@@ -4042,6 +4055,18 @@ the EVENTLOOP API.
}
vwait done
+The address, *addr*, can be given in one of the following forms:
+
+1. For IPv4 socket types, an IPv4 address such as 192.168.1.1
+2. For IPv6 socket types, an IPv6 address such as [fe80::1234] or [::]
+3. A hostname
+
+Note that on many systems, listening on an IPv6 address such as [::] will
+also accept requests via IPv4.
+
+Where a hostname is specified, the *first* returned address is used
+which matches the socket type is used.
+
The special type 'pipe' isn't really a socket.
lassign [socket pipe] r w