/*
 * inet/inet_pton.c
 */

#include <stdio.h>
#include <stdint.h>
#include <errno.h>
#include <ctype.h>
#include <string.h>
#include <arpa/inet.h>
#include <netinet/in6.h>

static inline int hexval(int ch)
{
	if (ch >= '0' && ch <= '9') {
		return ch - '0';
	} else if (ch >= 'A' && ch <= 'F') {
		return ch - 'A' + 10;
	} else if (ch >= 'a' && ch <= 'f') {
		return ch - 'a' + 10;
	} else {
		return -1;
	}
}

int inet_pton(int af, const char *src, void *dst)
{
	switch (af) {
	case AF_INET:
		return inet_aton(src, (struct in_addr *)dst);

	case AF_INET6:
		{
			struct in6_addr *d = (struct in6_addr *)dst;
			int colons = 0, dcolons = 0;
			int i;
			const char *p;

			/* A double colon will increment colons by 2,
			   dcolons by 1 */
			for (p = dst; *p; p++) {
				if (p[0] == ':') {
					colons++;
					if (p[1] == ':')
						dcolons++;
				} else if (!isxdigit(*p))
					return 0;	/* Invalid address */
			}

			if (colons > 7 || dcolons > 1
			    || (!dcolons && colons != 7))
				return 0;	/* Invalid address */

			memset(d, 0, sizeof(struct in6_addr));

			i = 0;
			for (p = dst; *p; p++) {
				if (*p == ':') {
					if (p[1] == ':') {
						i += (8 - colons);
					} else {
						i++;
					}
				} else {
					d->s6_addr16[i] =
					    htons((ntohs(d->s6_addr16[i]) << 4)
						  + hexval(*p));
				}
			}

			return 1;
		}

	default:
		errno = EAFNOSUPPORT;
		return -1;
	}
}
