--- /dev/null
+/*
+ * struct sockaddr
+ *
+ * syntaxes:
+ * ddd.ddd.ddd.ddd,nnnn IPv4 address and port
+ * ... host or port may be `*'
+ * /abs/path/to/socket AF_UNIX
+ * ./rel/path/to/socket AF_UNIX
+ */
+
+#include "hbytes.h"
+#include "tables.h"
+
+#define SOCKADDR_LEN(sa) ((sa)->end - (sa)->begin)
+
+/* parsing */
+
+int pat_sockaddr(Tcl_Interp *ip, Tcl_Obj *o, SockAddr_Value *val) {
+ int rc;
+
+ rc= Tcl_ConvertToType(ip,o,&sockaddr_type);
+ if (rc) return rc;
+
+ *val= *OBJ_SOCKADDR(o);
+ return TCL_OK;
+}
+
+Tcl_Obj *ret_sockaddr(Tcl_Interp *ip, SockAddr_Value val) {
+ Tcl_Obj *o;
+
+ o= Tcl_NewObj();
+ Tcl_InvalidateStringRep(o);
+ *OBJ_SOCKADDR(o)= val;
+ o->typePtr= &sockaddr_type;
+ return o;
+}
+
+/* native type methods */
+
+void sockaddr_clear(SockAddr_Value *v) { v->begin= v->end= 0; }
+
+void sockaddr_create(SockAddr_Value *v, const struct sockaddr *addr, int len) {
+ Byte *begin;
+
+ v->begin= begin= TALLOC(len);
+ memcpy(begin, addr, len);
+ v->end= begin + len;
+}
+
+int sockaddr_len(const SockAddr_Value *v) {
+ return SOCKADDR_LEN(v);
+}
+
+const struct sockaddr *sockaddr_addr(const SockAddr_Value *v) {
+ return (const void*)v->begin;
+}
+
+void sockaddr_free(const SockAddr_Value *v) {
+ TFREE(v->begin);
+}
+
+/* Sockaddr Tcl type */
+
+static void sockaddr_t_dup(Tcl_Obj *src, Tcl_Obj *dup) {
+ objfreeir(dup);
+ sockaddr_create(OBJ_SOCKADDR(dup),
+ sockaddr_addr(OBJ_SOCKADDR(src)),
+ sockaddr_len(OBJ_SOCKADDR(src)));
+}
+
+static void sockaddr_t_free(Tcl_Obj *o) {
+ sockaddr_free(OBJ_SOCKADDR(o));
+}
+
+static void sockaddr_t_ustr(Tcl_Obj *o) {
+ const struct sockaddr *sa;
+ char i46buf[INET6_ADDRSTRLEN];
+ int al, sl, pl;
+ const char *string, *prepend;
+
+ sa= sockaddr_addr(OBJ_SOCKADDR(o));
+ al= sockaddr_len(OBJ_SOCKADDR(o));
+
+ switch (sa->sa_family) {
+ case AF_INET:
+ case AF_INET6:
+ assert(sizeof(i46buf) >= INET_ADDRSTRLEN);
+ assert(al >= sizeof(struct sockaddr_in));
+ inet_ntop(sa->sa_family, sa, i46buf, al);
+ string= i46buf;
+ prepend= "";
+ break;
+
+ case AF_UNIX:
+ assert(al >= sizeof(struct sockaddr_un));
+ string= ((const struct sockaddr_un*)sa)->sun_path;
+ prepend= "";
+ if (!string[0]) string="//";
+ else if (string[0] != '/' || string[1] == '/') prepend= "./";
+
+ default: /* ouch ! */
+ obj_updatestr_array_prefix(o,(const void*)sa,al,"?");
+ return;
+ }
+
+ pl= strlen(prepend);
+ sl= strlen(string);
+ o->bytes= TALLOC(sl+1);
+ memcpy(o->bytes, prepend, pl);
+ memcpy(o->bytes+pl, string, sl+1);
+}
+
+static int sockaddr_t_sfa(Tcl_Interp *ip, Tcl_Obj *o) {
+ union {
+ struct sockaddr_un sun;
+ struct sockaddr_in sin;
+ } s;
+ unsigned long port_l;
+
+ char *str, *ep, *copy;
+ int sl, pl, iprv;
+ const char *comma, *path;
+
+ str= Tcl_GetStringFromObj(o,0); assert(str);
+ objfreeir(o);
+ memset(&s,0,sizeof(s));
+
+ if (str[0]=='/' || (str[0]=='.' && str[1]=='/')) {
+
+ sl= sizeof(s.sun);
+ s.sun.sun_family= AF_UNIX;
+
+ if (strcmp(str,"//")) path= "";
+ else if (memcmp(str,"./",2) && str[2]) path= str+2;
+ else { assert(str[0]=='/' && str[1]!='/'); path=str; }
+
+ if (strlen(str) >= sizeof(s.sun.sun_path))
+ return staticerr(ip, "AF_UNIX path too long");
+
+ strcpy(s.sun.sun_path, str);
+
+ } else if ((comma= strchr(str, ','))) {
+
+ sl= sizeof(s.sin);
+ s.sin.sin_family= AF_INET;
+
+ pl= comma - str;
+ copy= TALLOC(pl+1);
+ memcpy(copy, str, pl);
+ copy[pl]= 0;
+
+ if (!strcmp(copy,"*")) {
+ s.sin.sin_addr.s_addr= INADDR_ANY;
+ iprv= 1;
+ } else {
+ iprv= inet_pton(AF_INET, copy, &s.sin.sin_addr);
+ }
+ TFREE(copy);
+
+ if (!iprv) return staticerr(ip, "bad IPv4 address syntax");
+
+ comma++;
+ if (!strcmp(comma,"*")) {
+ s.sin.sin_port= 0;
+ } else {
+ errno=0; port_l=strtoul(comma,&ep,10);
+ if (errno || *ep) return staticerr(ip, "bad IPv4 port");
+ if (port_l > 65535) return staticerr(ip, "IPv4 port out of range");
+ s.sin.sin_port= htons(port_l);
+ }
+
+ } else {
+
+ return staticerr(ip, "bad socket address syntax");
+
+ }
+
+ sockaddr_create(OBJ_SOCKADDR(o), (void*)&s, sl);
+
+ o->typePtr = &sockaddr_type;
+ return TCL_OK;
+}
+
+Tcl_ObjType sockaddr_type = {
+ "sockaddr",
+ sockaddr_t_free, sockaddr_t_dup, sockaddr_t_ustr, sockaddr_t_sfa
+};