/* $Id$ */
/* Copyright (c) 2008-2016 Pierre Pronchery <khorben@defora.org> */
/* This file is part of DeforaOS System libc */
/* All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions are
 * met:
 *
 * 1. Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer.
 *
 * 2. Redistributions in binary form must reproduce the above copyright
 *    notice, this list of conditions and the following disclaimer in the
 *    documentation and/or other materials provided with the distribution.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
 * IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
 * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
 * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
 * HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED
 * TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
 * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
 * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
 * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */
/* FIXME check if correct then optimize with macros or whatever */



#include "sys/types.h"
#include "stdlib.h"
#include "stdio.h"
#include "errno.h"
#include "arpa/inet.h"


/* variables */
static const int _test = 0x1234;
static const char * _ptest = (char *)&_test;


/* functions */
/* htonl */
uint32_t htonl(uint32_t host32)
{
	if(*_ptest == 0x12)
		return host32;
	return ((host32 & 0xff) << 24) | ((host32 & 0xff00) << 8)
		| ((host32 & 0xff0000) >> 8) | ((host32 & 0xff000000) >> 24);
}


/* htons */
uint16_t htons(uint16_t host16)
{
	if(*_ptest == 0x12)
		return host16;
	return ((host16 & 0xff) << 8) | ((host16 & 0xff00) >> 8);
}


/* inet_addr */
in_addr_t inet_addr(const char *cp)
{
	struct in_addr ia;

	if(inet_aton(cp, &ia) != 1)
		return INADDR_NONE;
	return ia.s_addr;
}


/* inet_aton */
int inet_aton(const char *cp, struct in_addr *addr)
{
	unsigned long byte;
	char * p;
	int i;

	if(cp == NULL || cp[0] == '\0')
		return 0;
	addr->s_addr = 0;
	for(i = 0; i < 4; i++)
	{
		byte = strtoul(cp, &p, 0);
		if(*p != '.' && *p != '\0')
			return 0;
		if(byte > 0xff)
			return 0;
		addr->s_addr = (addr->s_addr << 8) | byte;
		if(*p == '\0')
		{
			addr->s_addr = htonl(addr->s_addr);
			return 1;
		}
		cp = ++p;
	}
	return 0;
}


/* inet_ntoa */
char * inet_ntoa(struct in_addr in)
{
	static char ret[16];

	if(inet_ntop(AF_INET, &in, ret, sizeof(ret)) == NULL)
		return NULL;
	return ret;
}


/* inet_ntop */
static char const * _ntop_inet(const struct in_addr * in, char * dst,
		socklen_t size);
static char const * _ntop_inet6(const struct in6_addr * in6, char * dst,
		socklen_t size);

char const * inet_ntop(int family, const void * src, char * dst, socklen_t size)
{
	const struct in_addr * in = src;
	const struct in6_addr * in6 = src;

	switch(family)
	{
		case AF_INET:
			return _ntop_inet(in, dst, size);
		case AF_INET6:
			return _ntop_inet6(in6, dst, size);
		default:
#ifdef EAFNOSUPPORT
			errno = EAFNOSUPPORT;
#else
			errno = EINVAL;
#endif
			return NULL;
	}
}

static char const * _ntop_inet(const struct in_addr * in, char * dst,
		socklen_t size)
{
	unsigned char const * b = (unsigned char const *)&in->s_addr;
	unsigned int i;
	unsigned int pos;
	unsigned int p;

	for(i = 0, pos = 0;; i++)
		if(i == sizeof(in->s_addr))
			break;
		else if((p = snprintf(&dst[pos], size - pos, "%s%u",
						(i > 0) ? "." : "", b[i]))
				>= size - pos)
		{
			errno = ENOSPC;
			return NULL;
		}
		else
			pos += p;
	return dst;
}

static char const * _ntop_inet6(const struct in6_addr * in6, char * dst,
		socklen_t size)
{
	uint16_t * b = (uint16_t *)&in6->s6_addr;
	unsigned int i;
	unsigned int pos;
	unsigned int p;

	for(i = 0, pos = 0;; i++)
		if(i == sizeof(in6->s6_addr))
			break;
		else if((p = snprintf(&dst[pos], size - pos, "%s%x",
						(i > 0) ? ":" : "", b[i]))
				>= size - pos)
			return NULL;
		else
			pos += p;
	return dst;
}


/* ntohl */
uint32_t ntohl(uint32_t net32)
{
	return htonl(net32);
}


/* ntohs */
uint16_t ntohs(uint16_t net16)
{
	return htons(net16);
}
