/* Copyright 2017-present Renesas Electronics Corporation and other contributors
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

#include <stdio.h>
#include <string.h>
#include <stdarg.h>
#include <errno.h>
#include <assert.h>
#include <algorithm>
#include <string>
#include <iterator>
#include <vector>
#include <list>
#include <map>

extern "C" {
	//#include <unistd.h>
	int close(int fd);
	ssize_t read(int fd, void* buf, size_t count);
	ssize_t write(int fd, const void* buf, size_t count);
	off_t lseek(int fd, off_t offset, int whence);
	int unlink(const char* pathname);
}	// extern "C"

#include <mbed.h>
#include "mbed_retarget.h"
#include "hal/us_ticker_api.h"
#include "posix4mbed.h"
#include "peripheral_io_4mbed.h"
#include "primitives.h"

using namespace os::primitives;
#define SocketAddress os::primitives::SocketAddress

#define NAME_STRINGIZER_(name) #name
#define NAME_STRINGIZER(name) NAME_STRINGIZER_(name)

const struct P4(in6_addr) in6addr_any = {0};

namespace helper {

	#if defined(DEFAULT_WORKING_DIR)
		std::string cwd_ = NAME_STRINGIZER(DEFAULT_WORKING_DIR);
	#else	// if defined(DEFAULT_WORKING_DIR)
		std::string cwd_ = "/";
	#endif	// if defined(DEFAULT_WORKING_DIR)

	std::string& cwd() {
		return cwd_;
	}

	int clear_errno() {
		errno = 0;
		return 0;
	}

	int set_errno(int num) {
		if( ENOSYS == num ) {
			TRACE(INFO "ENOSYS ......");
		}
		errno = num;
		return -1;
	}

	uint64_t time_in_usec() {
		struct pack {
			uint64_t usec;	// in micro-seconds
			uint64_t unix;	// in micro-seconds
			uint64_t tick;	// in micro-seconds
		};
		static pack last = {0};
		pack curr = {0};
		curr.unix = time( 0 ) * uint64_t( 1 * 1000 * 1000 );
		curr.tick = us_ticker_read() * 32 * 3 / 100;

		static bool first_time = true;
		if( first_time || ( curr.tick < last.tick ) || ( curr.unix < last.unix ) ) {
			first_time = false;
			curr.usec = curr.unix;
		}
		else {
			pack diff;
			diff.unix = curr.unix - last.unix;
			diff.tick = curr.tick - last.tick;
			diff.usec = diff.tick;

			uint64_t range = ( 1 * 1000 * 1000 ) + diff.usec - diff.unix;
			if( range <= 2 * 1000 * 1000 ) {
				curr.usec = last.usec + diff.usec;
			}
			else {
				curr.usec = curr.unix;
			}
		}

		last = curr;
		return curr.usec;
	}

	class lseeker {
		int fd_;
		P4(off_t) saved_;
	public:
		lseeker(int fd) : fd_(fd), saved_(-1) {}
		bool go(P4(off_t) offset) {
			saved_ = ::lseek( fd_, 0, SEEK_CUR );
			return ( 0 <= saved_ ) && ( 0 <= ::lseek( fd_, offset, SEEK_SET ) );
		}
		bool back() {
			return ( 0 <= saved_ ) && ( 0 <= ::lseek( fd_, saved_, SEEK_SET ) );
		}
	};

	ip_addr_t ip4addr(u8_t a, u8_t b, u8_t c, u8_t d) {
		ip_addr_t rc = { ip_addr_t::IPv4, 0 };
		rc.value.bytes[0] = a;
		rc.value.bytes[1] = b;
		rc.value.bytes[2] = c;
		rc.value.bytes[3] = d;
		return rc;
	}

	ip_addr_t ip6addr(u16_t a, u16_t b, u16_t c, u16_t d, u16_t e, u16_t f, u16_t g, u16_t h) {
		ip_addr_t rc = { ip_addr_t::IPv6, 0 };
		rc.value.u16[0] = htons( a );
		rc.value.u16[1] = htons( b );
		rc.value.u16[2] = htons( c );
		rc.value.u16[3] = htons( d );
		rc.value.u16[4] = htons( e );
		rc.value.u16[5] = htons( f );
		rc.value.u16[6] = htons( g );
		rc.value.u16[7] = htons( h );
		return rc;
	}

	ip_addr_t ip6addr(const u8_t bytes[]) {
		ip_addr_t rc = { ip_addr_t::IPv6, 0 };
		memcpy( rc.value.bytes, bytes, sizeof( rc.value.bytes ) );
		return rc;
	}

	ip_addr_t ipaddr(const nsapi_addr_t& na) {
		TRACE(INFO "nsapi_addr.version=%d bytes[%d.%d.%d...]", na.version, na.bytes[0], na.bytes[1], na.bytes[2]);
		switch( na.version )
		{
		default:		 return { ip_addr_t::UNSPEC, 0 };
		case NSAPI_IPv4: return ip4addr( na.bytes[0], na.bytes[1], na.bytes[2], na.bytes[3] );
		case NSAPI_IPv6: return ip6addr( na.bytes );
		}
	}

	inline ip_addr_t ipaddr_any(bool ipv6) {
		return ipv6 ? ip6addr( 0,0,0,0,0,0,0,0 ) : ip4addr( 0,0,0,0 );
	}

	inline ip_addr_t ipaddr_loopback(bool ipv6) {
		return ipv6 ? ip6addr( 0,0,0,0,0,0,0,1 ) : ip4addr( 127,0,0,1 );
	}

	Nic nic;

}	// namespace helper::

namespace os { namespace posix { namespace pthread {

	union thread_posix_raw {
		P4(pthread_t) posix;
		Thread* raw;
		thread_posix_raw(P4(pthread_t) p) : posix( p ) {}
	};

	union func_posix_raw {
		void* (*posix)(void*);
		void (*raw)(void const*);
		func_posix_raw(void* (*p)(void*)) : posix( p ) {}
	};

	union mutex_posix_raw {
		P4(pthread_mutex_t) posix;
		Mutex* raw;
		mutex_posix_raw(P4(pthread_mutex_t)* p) : posix( *p ) {}
	};

	union condvar_posix_raw {
		P4(pthread_cond_t) posix;
		CondVar* raw;
		condvar_posix_raw(P4(pthread_cond_t)* p) : posix( *p ) {}
	};

}}}	// namespace os::posix::pthread::


namespace iotjs { namespace bugfix {

	nsapi_size_or_error_t send(TCPSocket& socket, const void* buf, P4(size_t) count) {
		const char* top = static_cast<const char*>( buf );
		int remain = count;
		while( 0 < remain ) {
			nsapi_size_or_error_t rc = socket.send( top, remain );
			if( NSAPI_ERROR_WOULD_BLOCK == rc ) {
				TRACE(INFO "socket.send(%p, %d) again", top, remain);
				continue;
			}
			if( rc < 0 ) {
				return rc;
			}
			remain -= rc;
			top += rc;
		}
		return count;
	}

}}	// namespace iotjs::bugfix::


namespace os { namespace linux { namespace epoll {

	Semaphore poll_;

	class fd_builder {
		Mutex mutex_;
		int from_, to_;
		std::vector< bool > using_;
	public:
		fd_builder( int from, int to )
		: mutex_(), from_( from ), to_( to ), using_( to - from + 1, false ) {
		}
		int allocate() {
			lock_guard g( mutex_ );
			auto itr = std::find( using_.rbegin(), using_.rend(), false );
			if( itr == using_.rend() ) {
				return -1;
			}
			*itr = true;
			return from_ + std::distance( begin( using_ ), itr.base() ) - 1;
		}
		bool release( int fd ) {
			lock_guard g( mutex_ );
			if( ( fd < from_ ) || ( to_ < fd ) ) {
				return false;
			}
			using_[ fd - from_ ] = false;
			return true;
		}
	} fd_builder_( 100, 250 );	// fd range[100, 250]

	class evt_counter {
		Mutex mutex_;
		int count_;
		evt_counter( const evt_counter& );
		evt_counter& operator=( const evt_counter& );
	public:
		evt_counter() : mutex_(), count_(0) {}
		void add() {
			mutex_.lock();
			count_ += 1;
			poll_.release();
			mutex_.unlock();
		}
		bool check() {
			bool rc = false;
			mutex_.lock();
			if( count_ > 0 ) {
				rc = true;
				TRACE(INFO "evt_counter : SOMETHING HAPPENED : %d", count_);
				count_ -= 1;
			}
			mutex_.unlock();
			return rc;
		}
	};

	typedef std::map<int, P4(poll_event)> poll_internal;
	typedef std::map<int, poll_internal> EPollFDs;
	EPollFDs epfds_;
	inline bool isEPollFd(int fd) { return epfds_.find( fd ) != epfds_.end(); }
	inline int newEPollFd(int flags) { int fd = fd_builder_.allocate(); if(0<fd) epfds_[ fd ].clear(); return fd; }
	inline void removeEPollFd(int fd) { epfds_.erase( fd ); fd_builder_.release( fd ); }
	inline poll_internal& getEPollInfo(int fd) { return epfds_[ fd ]; }

	typedef uint64_t eventfd_t;
	typedef std::map<int, eventfd_t> EventFDs;
	EventFDs evfds_;
	inline bool isEventFd(int fd) { return evfds_.find( fd ) != evfds_.end(); }
	inline int newEventFd(unsigned int initval) { int fd = fd_builder_.allocate(); if(0<fd) evfds_[ fd ] = initval; return fd; }
	inline void removeEventFd(int fd) { evfds_.erase( fd ); fd_builder_.release( fd ); }
	inline void resetEventFd(int fd) { evfds_[ fd ] = 0; }
	inline void plusEventFd(int fd, eventfd_t value) { evfds_[ fd ] += value; }
	inline eventfd_t& getEventFd(int fd) { return evfds_[ fd ]; }

	class sock_t {
		enum { UNKNOWN, UDP, TCP, SRV } type_;
		int shutdown_;
		evt_counter events_;
		UDPSocket udp_;
		TCPSocket tcp_;
		TCPServer srv_;
		SocketAddress addr_;
		sock_t( const sock_t& );
		sock_t& operator=( const sock_t& );
	public:
		sock_t() : type_( UNKNOWN ), shutdown_( -1 ) {}
		bool isUDP() const { return type_ == UDP; }
		bool isTCP() const { return type_ == TCP; }
		bool isSRV() const { return type_ == SRV; }
		sock_t& asUDP() { type_ = UDP; return *this; }
		sock_t& asTCP() { type_ = TCP; return *this; }
		sock_t& asSRV() { type_ = SRV; return *this; }
		UDPSocket& udp() { return udp_; }
		TCPSocket& tcp() { return tcp_; }
		TCPServer& srv() { return srv_; }
		const SocketAddress& addr() const { return addr_; }
		SocketAddress& addr() { return addr_; }
		SocketAddress& addr(const P4(sockaddr)& addr) { return addr_ = SocketAddress::trans( addr ); }
		int open();
		int attach();
		bool evt_check() { return events_.check(); }
		void evt_add() { return events_.add(); }
		int shutdown() const { return shutdown_; }
		int shutdown(int how);
	};
	typedef std::map<int, sock_t> SockFDs;
	SockFDs sockfds_;
	inline bool isSockFd(int fd) { return sockfds_.find( fd ) != sockfds_.end(); }
	inline sock_t& getSock(int fd) { return sockfds_[ fd ]; }
	inline sock_t& newSock(int fd) { return sockfds_[ fd ]; }
	inline void removeSockFd(int fd) { sockfds_.erase( fd ); fd_builder_.release( fd ); }
	inline bool isUdpSockFd(int fd) { return isSockFd( fd ) && getSock( fd ).isUDP(); }
	inline bool isTcpSockFd(int fd) { return isSockFd( fd ) && getSock( fd ).isTCP(); }
	inline bool isSrvSockFd(int fd) { return isSockFd( fd ) && getSock( fd ).isSRV(); }

	inline bool isRelatedFd(int fd) { return isEPollFd( fd ) || isEventFd( fd ) || isSockFd( fd ); }

	int eventfd_read(int fd, eventfd_t* value) {
		TRACE(ENTER "eventfd_read(fd=%d, value=%p)", fd, value);
		int rc = -1;
		eventfd_t prev_counter = getEventFd( fd );
		if( prev_counter == 0 ) {
			rc = helper::set_errno( EAGAIN );	// EFD_NONBLOCK
		}
		else {
			*value = prev_counter;
			resetEventFd( fd );	// ! EFD_SEMAPHORE
			rc = helper::clear_errno();
			poll_.release();	// POLLOUT
		}
		TRACE(LEAVE "eventfd_read(fd=%d, value={%llu}%llu->%llu) = %d(errno:%d)", fd, *value, prev_counter, getEventFd( fd ), rc, errno);
		return rc;
	}

	int eventfd_write(int fd, eventfd_t value) {
		TRACE(ENTER "eventfd_write(fd=%d, value=%llu)", fd, value);
		int rc = -1;
		eventfd_t prev_counter = getEventFd( fd );
		if( value + 1 == 0 ) {
			rc = helper::set_errno( EINVAL );
		}
		else {
			if( prev_counter + value < prev_counter ) {
				rc = helper::set_errno( EAGAIN );	// EFD_NONBLOCK
			}
			else {
				plusEventFd( fd, value );
				rc = helper::clear_errno();
				poll_.release();	// POLLIN
			}
		}
		TRACE(LEAVE "eventfd_write(fd=%d, value=%llu->%llu) = %d(errno:%d)", fd, prev_counter, getEventFd( fd ), rc, errno);
		return rc;
	}

	P4(ssize_t) socketfd_read(int fd, void* buf, P4(size_t) count) {
		if( isTcpSockFd( fd ) ) {
			TRACE(INFO "socketfd_read() with TCP sock fd=%d", fd);
			nsapi_size_or_error_t ret = 0;
			sock_t& sock = getSock( fd );
			int shutdown = sock.shutdown();
			if( shutdown == SHUT_RD || shutdown == SHUT_RDWR ) {
				TRACE(INFO "socketfd_read() with TCP sock fd=%d shutdown=%d", fd, shutdown);
				ret = 0;
			}
			else {
				TCPSocket& socket = sock.tcp();
				ret = socket.recv( buf, count );
			}
			TRACE(INFO "socketfd_read() with TCP sock fd=%d nsapi_error=%d", fd, ret);
			if( NSAPI_ERROR_WOULD_BLOCK == ret ) {
				return helper::set_errno( EWOULDBLOCK );
			}
			else if( ret < 0 ) {
				return helper::set_errno( EINVAL );
			}
			return ret;
		}
		else if( isSrvSockFd( fd ) ) {
			TRACE(INFO "socketfd_read() with SRV sock fd=%d", fd);
			return helper::set_errno( EINVAL );
		}
		else if( isUdpSockFd( fd ) ) {
			TRACE(INFO "socketfd_read() with UDP sock fd=%d", fd);
			return helper::set_errno( EINVAL );
		}
		return helper::set_errno( ENOSYS );
	}

	P4(ssize_t) socketfd_write(int fd, const void* buf, P4(size_t) count) {
		if( isTcpSockFd( fd ) ) {
			TRACE(INFO "socketfd_write() with TCP sock fd=%d first=%02Xh count=%d", fd, *((char*)buf), count);
			nsapi_size_or_error_t ret = 0;
			sock_t& sock = getSock( fd );
			int shutdown = sock.shutdown();
			if( shutdown == SHUT_WR || shutdown == SHUT_RDWR ) {
				TRACE(INFO "socketfd_write() with TCP sock fd=%d shutdown=%d", fd, shutdown);
				ret = 0;
			}
			else {
				TCPSocket& socket = sock.tcp();
				ret = iotjs::bugfix::send( socket, buf, count ); // socket.send( buf, count );
			}
			TRACE(INFO "socketfd_write() with TCP sock fd=%d first=%02Xh count=%d nsapi_error=%d", fd, *((char*)buf), count, ret);
			if( NSAPI_ERROR_WOULD_BLOCK == ret ) {
				return helper::set_errno( EWOULDBLOCK );
			}
			if( ret < 0 ) {
				return helper::set_errno( EINVAL );
			}
			return ret;
		}
		else if( isSrvSockFd( fd ) ) {
			TRACE(INFO "socketfd_write() with SRV sock fd=%d", fd);
			return helper::set_errno( EINVAL );
		}
		else if( isUdpSockFd( fd ) ) {
			TRACE(INFO "socketfd_write() with UDP sock fd=%d", fd);
			return helper::set_errno( EINVAL );
		}
		return helper::set_errno( ENOSYS );
	}

	union ptr_void_evfd {
		const void* cv;
		void* v;
		eventfd_t* evfd;
	};

	P4(ssize_t) read(int fd, void* buf, P4(size_t) count) {
		if( isEventFd( fd ) ) {
			if( count < sizeof( eventfd_t ) ) {
				return helper::set_errno( EINVAL );
			}
			ptr_void_evfd ptr;
			ptr.v = buf;
			if( eventfd_read( fd, ptr.evfd ) ) {
				return -1;
			}
			return sizeof( eventfd_t );
		}
		else if( isSockFd( fd ) ) {
			return socketfd_read( fd, buf, count );
		}
		TRACE(INFO "epoll::read() with wrong fd=%d", fd);
		return helper::set_errno( EINVAL );
	}

	P4(ssize_t) write(int fd, const void* buf, P4(size_t) count) {
		if( isEventFd( fd ) ) {
			if( count < sizeof( eventfd_t ) ) {
				return helper::set_errno( EINVAL );
			}
			ptr_void_evfd ptr;
			ptr.cv = buf;
			if( eventfd_write( fd, *ptr.evfd ) ) {
				return -1;
			}
			return sizeof( eventfd_t );
		}
		else if( isSockFd( fd ) ) {
			return socketfd_write( fd, buf, count );
		}
		TRACE(INFO "epoll::write() with wrong fd=%d", fd);
		return helper::set_errno( EINVAL );
	}

	int close(int fd) {
		if( isEPollFd( fd ) ) {
			removeEPollFd( fd );
			return helper::clear_errno();
		}
		else if( isEventFd( fd ) ) {
			removeEventFd( fd );
			poll_.release();	// POLLHUP
			return helper::clear_errno();
		}
		else if( isSockFd( fd ) ) {
			removeSockFd( fd );
			return helper::clear_errno();
		}
		TRACE(INFO "epoll::close() with wrong fd=%d", fd);
		return helper::set_errno( ENOSYS );
	}

	int sock_t::open() {
		Socket* socket = 0;
		     if( isSRV() ) { socket = &srv(); }
		else if( isTCP() ) { socket = &tcp(); }
		else if( isUDP() ) { socket = &udp(); }
		else {
			return helper::set_errno( EINVAL );
		}
		EthernetInterface& stack = helper::nic.iface();
		socket->sigio( callback( &events_, &evt_counter::add ) );
		nsapi_error_t ret = socket->open( &stack );
		if( ret < 0 ) {
			TRACE(INFO "sock Socket::open()={%d}", ret);
			return helper::set_errno( ENFILE );
		}
		socket->set_blocking( false );
		return helper::clear_errno();
	}

	int sock_t::attach() {
		Socket* socket = 0;
		if( isTCP() ) { socket = &tcp(); }
		else {
			return helper::set_errno( ENFILE );
		}
		socket->sigio( callback( &events_, &evt_counter::add ) );
		socket->set_blocking( false );
		evt_add();
		evt_add();
		return helper::clear_errno();
	}

	int sock_t::shutdown(int how) {
		shutdown_ = how;
		bool ret = ( tcp().send( NAME_STRINGIZER(VIRTUAL_FIN_PACKET), 1 + how ) == 1 + how );
		evt_add();
		return ret ? 0 : -1;
	}

}}}	// namespace os::linux::epoll::


namespace os { namespace bugfix {

	typedef struct stat stat_t;

	int stat(const char* pathname, stat_t* buf) {
		if( !pathname || !pathname[0] ) {
			return helper::set_errno( ENOENT );
		}
		int rc = ::stat( pathname, buf );
		if( rc < 0 && errno == 0 ) {
			return helper::set_errno( ENOENT );
		}
		return rc;
	}

	int fstat(int fd, stat_t* buf) {
		if( fd < 0 ) {
			return helper::set_errno( EBADF );
		}
		if( fd < 3 ) {
			buf->st_mode = S_IFCHR;
			return helper::clear_errno();
		}
		struct {
			int operator()(int fd) {
				int cur = ::lseek( fd, 0, SEEK_CUR );
				int end = ::lseek( fd, 0, SEEK_END );
				::lseek( fd, cur, SEEK_SET );
				return end;
			}
		} file_size;
		int size = file_size( fd );
		if( size < 0 ) {
			return helper::set_errno( EBADF );
		}
		buf->st_mode = S_IFREG;
		buf->st_size = size;
		return helper::clear_errno();
	}

	int open(const std::string& pathname, va_list args) {
		if( pathname == "/" ) {
			return helper::set_errno( ENOENT );
		}
		else if( pathname == "/dev/null" ) {
			return helper::set_errno( ENOENT );
		}
		return ::open( pathname.c_str(), va_arg(args, int), va_arg(args, mode_t) );
	}

	int close(int fd) {
		if( fd < 0 ) {
			return helper::set_errno( EBADF );
		}
		return ::close( fd );
	}

}}	// namespace os::bugfix::


#define NOT_IMPL(detail)	detail { return 0; }

void os::debug::trace(const char* fmt, ...)
{
#if defined(DEBUG_POSIX_BRIDGE)
	static struct {
		int sequence;
		Mutex mutex;
		std::map< void*, int > threads;
	} trace_;

	int saved_errno = errno;
	trace_.mutex.lock();

	++ trace_.sequence;
	void* id = Thread::gettid();
	if( trace_.threads.find( id ) == trace_.threads.end() ) {
		trace_.threads[ id ] = trace_.threads.size() - 1;
	}

	printf( "--- POSIX[%3u][thr%d] %*s",
		trace_.sequence % 1000, trace_.threads[ id ], trace_.threads[ id ] * 3, "" );
	va_list args;
	va_start( args, fmt );
	vfprintf( stdout, fmt, args );
	va_end( args );
	printf( "\n" );

	trace_.mutex.unlock();
	errno = saved_errno;
#endif	// #if defined(DEBUG_POSIX_BRIDGE)
}

int os::posix::obsolete::gettimeofday(P4(timeval)* tv, timezone* tz)
{
	if( !tv && !tz ) {
		return helper::set_errno( EFAULT );
	}
	if( tv ) {
		P4(timespec) tp;
		if( clock::gettime( 123, &tp ) < 0 ) {
			return helper::set_errno( EFAULT );
		}
		tv->tv_sec  = tp.tv_sec;
		tv->tv_usec = tp.tv_nsec / 1000;
	}
	if( tz ) {
		tz->tz_minuteswest = 0;
		tz->tz_dsttime = 0;
	}
	return helper::clear_errno();
}

char* os::posix::getcwd(char* buf, P4(size_t) size)
{
	if( !buf || size <= helper::cwd().size() ) {
		helper::set_errno( EFAULT );
		return 0;
	}
	return strcpy( buf, helper::cwd().c_str() );
}

int os::posix::chdir(const char* path)
{
	helper::cwd() = path;
	return helper::clear_errno();
}

NOT_IMPL( int os::posix::fdatasync(int fd) );
NOT_IMPL( int os::posix::fsync(int fd) );
NOT_IMPL( int os::posix::ftruncate(int fd, P4(off_t) length) );

int os::posix::scandir(const char* dirp, P4(dirent)*** namelist, int (*filter)(const P4(dirent)*), int (*compar)(const P4(dirent)**, const P4(dirent)**))
{
	DIR* dir = opendir( dirp );
	if( !dir ) {
		return helper::set_errno( ENOENT );
	}

	std::list<P4(dirent)*> items;
	for( dirent* entry = readdir( dir ); entry; entry = readdir( dir ) ) {
		P4(dirent) temp;
		strcpy( temp.d_name, entry->d_name );
		temp.d_type = entry->d_type;
		if( filter && !(*filter)( &temp ) ) {
			continue;
		}
		P4(dirent)* item = static_cast<P4(dirent)*>( malloc( sizeof( P4(dirent) ) ) );
		if( !item ) {
			std::for_each( items.begin(), items.end(), free );
			return helper::set_errno( ENOMEM );
		}
		*item = temp;
		items.push_back( item );
	}
	closedir( dir );

	P4(dirent)** names = 0;
	P4(size_t) count = items.size();
	if( count ) {
		P4(size_t) bytes = count * sizeof( P4(dirent)* );
		names = static_cast<P4(dirent)**>( malloc( bytes ) );
		if( !names ) {
			std::for_each( items.begin(), items.end(), free );
			return helper::set_errno( ENOMEM );
		}
		std::copy( items.begin(), items.end(), names );

		if( count && compar ) {
			union {
				int (*param)(const P4(dirent)**, const P4(dirent)**);
				int (*qsort)(const void*, const void*);
			} func;
			func.param = compar;
			qsort( names, count, sizeof( P4(dirent)* ), func.qsort );
		}
	}
#if defined(DEBUG_POSIX_BRIDGE)
	for( P4(size_t) i = 0; i < count; ++ i ) {
		TRACE(INFO "scandir[%u]={%s}", i, names[ i ]->d_name );
	}
#endif	// #if defined(DEBUG_POSIX_BRIDGE)

	*namelist = names;
	return count;
}

NOT_IMPL( int os::posix::utime(const char* filename, const P4(utimbuf)* times) );
NOT_IMPL( int os::posix::utimes(const char* filename, const P4(timeval) times[2]) );

P4(ssize_t) os::posix::pread(int fd, void* buf, P4(size_t) count, P4(off_t) offset)
{
	helper::lseeker seeker( fd );
	if( !seeker.go( offset ) ) {
		return -1;
	}
	P4(ssize_t) rc = ::read( fd, buf, count );
	return seeker.back() ? rc : -1;
}

P4(ssize_t) os::posix::pwrite(int fd, const void* buf, P4(size_t) count, P4(off_t) offset)
{
	helper::lseeker seeker( fd );
	if( !seeker.go( offset ) ) {
		return -1;
	}
	P4(ssize_t) rc = ::write( fd, buf, count );
	return seeker.back() ? rc : -1;
}

NOT_IMPL( P4(ssize_t) os::posix::readv(int fd, const P4(iovec)* iov, int iovcnt) );
NOT_IMPL( P4(ssize_t) os::posix::writev(int fd, const P4(iovec)* iov, int iovcnt) );
NOT_IMPL( int os::posix::utimensat(int dirfd, const char* pathname, const P4(timespec) times[2], int flags) );

int os::posix::clock::getres(P4(clockid_t) clk_id, P4(timespec)* res)
{
	if( !res ) {
		return helper::set_errno( EFAULT );
	}
	res->tv_sec  = 0;
	res->tv_nsec = 1 * 1000 * 1000;	// 1 millisecond
	return helper::clear_errno();
}

int os::posix::clock::gettime(P4(clockid_t) clk_id, P4(timespec)* tp)
{
	if( !tp ) {
		return helper::set_errno( EFAULT );
	}
	uint64_t msec = helper::time_in_usec() / 1000;
	tp->tv_sec = static_cast< time_t >( msec / 1000 );
	msec %= 1000;
	tp->tv_nsec = static_cast< long >( msec * 1000 * 1000 );	// in nano-seconds
	return helper::clear_errno();
}

int os::posix::pthread::create(P4(pthread_t)* thread, P4(pthread_attr_t)* attr, void* (*start_routine)(void*), void* arg)
{
	if( !thread ) {
		return EINVAL;
	}
	func_posix_raw func( start_routine );
	const uint32_t stack_size = 16 * 1024;
	Thread* thr = new Thread( osPriorityNormal, stack_size );
	*thread = thr;
	thr->start( callback( func.raw, arg ) );
	return 0;
}

int os::posix::pthread::join(P4(pthread_t) thread, void** retval)
{
	if( !thread ) {
		return EINVAL;
	}
	thread_posix_raw thr( thread );
	thr.raw->join();
	return 0;
}

int os::posix::pthread::once(P4(pthread_once_t)* once_control, void (*init_routine)(void))
{
	if( once_control && *once_control == PTHREAD_ONCE_INIT ) {
		if( init_routine ) {
			(*init_routine)();
		}
		*once_control = ~PTHREAD_ONCE_INIT;
	}
	return 0;
}

NOT_IMPL( int os::posix::pthread::sigmask(int how, const P4(sigset_t)* set, P4(sigset_t)* oldset) );
NOT_IMPL( int os::posix::pthread::attr::destroy(P4(pthread_attr_t)* attr) );

int os::posix::pthread::cond::destroy(P4(pthread_cond_t)* cond)
{
	if( !cond ) {
		return EBUSY;
	}
	condvar_posix_raw condvar( cond );
	delete condvar.raw;
	return 0;
}

int os::posix::pthread::cond::init(P4(pthread_cond_t)* cond, P4(pthread_condattr_t)* cond_attr)
{
	if( cond ) {
		*cond = new CondVar;
	}
	return 0;
}

int os::posix::pthread::cond::signal(P4(pthread_cond_t)* cond)
{
	if( cond ) {
		condvar_posix_raw condvar( cond );
		condvar.raw->notify_one();
	}
	return 0;
}

int os::posix::pthread::cond::wait(P4(pthread_cond_t)* cond, P4(pthread_mutex_t)* mutex)
{
	if( cond ) {
		condvar_posix_raw condvar( cond );
		condvar.raw->wait( mutex );
	}
	return 0;
}

NOT_IMPL( int os::posix::pthread::condattr::destroy(P4(pthread_condattr_t)* attr) );
NOT_IMPL( int os::posix::pthread::condattr::init(P4(pthread_condattr_t)* attr) );
NOT_IMPL( int os::posix::pthread::condattr::setclock(P4(pthread_condattr_t)* attr, P4(clockid_t) clock_id) );

int os::posix::pthread::mutex::destroy(P4(pthread_mutex_t)* mutex)
{
	if( !mutex ) {
		return EBUSY;
	}
	mutex_posix_raw mtx( mutex );
	delete mtx.raw;
	return 0;
}

int os::posix::pthread::mutex::init(P4(pthread_mutex_t)* mutex, const P4(pthread_mutexattr_t)* mutexattr)
{
	if( mutex ) {
		*mutex = new Mutex;
	}
	return 0;
}

int os::posix::pthread::mutex::lock(P4(pthread_mutex_t)* mutex)
{
	if( !mutex ) {
		return EINVAL;
	}
	mutex_posix_raw mtx( mutex );
	mtx.raw->lock();
	return 0;
}

int os::posix::pthread::mutex::unlock(P4(pthread_mutex_t)* mutex)
{
	if( !mutex ) {
		return EINVAL;
	}
	mutex_posix_raw mtx( mutex );
	mtx.raw->unlock();
	return 0;
}

NOT_IMPL( int os::posix::pthread::rwlock::destroy(P4(pthread_rwlock_t)* rwlock) );
NOT_IMPL( int os::posix::pthread::rwlock::init(P4(pthread_rwlock_t)* rwlock, const P4(pthread_rwlockattr_t)* attr) );
NOT_IMPL( int os::posix::pthread::rwlock::rdlock(P4(pthread_rwlock_t)* rwlock) );
NOT_IMPL( int os::posix::pthread::rwlock::unlock(P4(pthread_rwlock_t)* rwlock) );

int os::posix::sock::accept(int sockfd, P4(sockaddr)* addr, P4(socklen_t)* addrlen)
{
	return P4(accept4)( sockfd, addr, addrlen, 0 );
}

int os::posix::sock::bind(int sockfd, const P4(sockaddr)* addr, P4(socklen_t) addrlen)
{
	if( addr->sa_family == AF_INET ) {
		if( linux::epoll::isUdpSockFd( sockfd ) ) {
			linux::epoll::sock_t& sock = linux::epoll::getSock( sockfd );
			sock.addr( *addr );
			TRACE(INFO "bind addr={%s:%d}", sock.addr().get_ip_address(), sock.addr().get_port());
			sock.udp().bind( sock.addr() );
		sock.evt_add();
		sock.evt_add();
			return helper::clear_errno();
		}
		else if( linux::epoll::isTcpSockFd( sockfd ) ) {
			linux::epoll::sock_t& sock = linux::epoll::getSock( sockfd ).asSRV();
			if( sock.open() < 0 ) {
				return helper::set_errno( EINVAL );
			}
			sock.addr( *addr );
			TRACE(INFO "bind addr={%s:%d}", sock.addr().get_ip_address(), sock.addr().get_port());
			sock.srv().bind( sock.addr() );
			return helper::clear_errno();
		}
	}
	return helper::set_errno( EINVAL );
}

int os::posix::sock::connect(int sockfd, const P4(sockaddr)* addr, P4(socklen_t) addrlen)
{
	if( !linux::epoll::isTcpSockFd( sockfd ) ) {
		return helper::set_errno( EBADF );
	}
	linux::epoll::sock_t& sock = linux::epoll::getSock( sockfd );
	sock.addr( *addr );
	TRACE(INFO "connecting to addr={%s:%d}", sock.addr().get_ip_address(), sock.addr().get_port());
	nsapi_error_t ret = sock.tcp().connect( sock.addr() );
	if( ret < 0 ) {
		TRACE(INFO "TCPSocket.connect() returned=%d", ret);
		return helper::set_errno( EFAULT );
	}
sock.evt_add();
	return helper::clear_errno();
}

void os::posix::sock::freeaddrinfo(P4(addrinfo)* res)
{
	while( res ) {
		P4(addrinfo)* next = res->ai_next;
		free( res );
		res = next;
	}
}

int os::posix::sock::getaddrinfo(const char* node, const char* service, const P4(addrinfo)* hints, P4(addrinfo)** res)
{
	if( !res ) {
		return EAI_FAIL;
	}
	*res = NULL;

	if( !node && !service ) {
		return EAI_NONAME;
	}

	int ai_family = AF_UNSPEC;
	if( hints ) {
		ai_family = hints->ai_family;
		switch( ai_family ) {
		default:		return EAI_FAMILY;
		case AF_UNSPEC:
		case AF_INET:
		case AF_INET6:	break;
		}
	}

	int port_host = 0;
	if( service ) {
		port_host = atoi( service );
		if( (port_host <= 0) || (0xFFFF < port_host) ) {
			return EAI_SERVICE;
		}
	}

	ip_addr_t addr = { ip_addr_t::UNSPEC, 0 };
	if( node ) {
		SocketAddress sa;
		nsapi_error_t err = helper::nic.iface().gethostbyname( node, &sa );
		if( err ) {
			TRACE(INFO "nsapi_error_t err=%d", err);
			return EAI_NONAME;
		}
		addr = helper::ipaddr( sa.get_addr() );
		if( (addr.v6() && ai_family == AF_INET ) ||
			(addr.v4() && ai_family == AF_INET6) ) {
			return EAI_NONAME;
		}
	}
	else {
		if( hints && ( hints->ai_flags & AI_PASSIVE ) ) {
			addr = helper::ipaddr_any( ai_family == AF_INET6 );
		}
		else {
			addr = helper::ipaddr_loopback( ai_family == AF_INET6 );
		}
	}
	if( !addr.valid() ) {
		TRACE(INFO "addr is not valid");
		return EAI_FAIL;
	}

	struct response_t {
		P4(addrinfo) ai;
		P4(sockaddr_storage) sas;
		char canonname[1];
	};

	P4(size_t) total_size = sizeof( response_t ) + ( node ? strlen( node ) : 0 );
	void* buf = malloc( total_size );
	if( !buf ) {
		return EAI_MEMORY;
	}
	memset( buf, 0, total_size );

	response_t& response = *static_cast< response_t* >( buf );
	P4(addrinfo)& ai			= response.ai;
	P4(sockaddr_storage)& sas	= response.sas;
	char* canonname			= response.canonname;

	helper::sockaddrs addrs;
	addrs.sas = &sas;
	if( addr.v4() ) {
		P4(sockaddr_in)& sa4 = *addrs.sa4;
		sa4.sin_addr = addr.value.ip4;
		sa4.sin_family = AF_INET;
		sa4.sin_len = sizeof( P4(sockaddr_in) );
		sa4.sin_port = htons( port_host );

		ai.ai_family = AF_INET;
	}
	else {
		P4(sockaddr_in6)& sa6 = *addrs.sa6;
		sa6.sin6_addr = addr.value.ip6;
		sa6.sin6_family = AF_INET6;
		sa6.sin6_len = sizeof( P4(sockaddr_in6) );
		sa6.sin6_port = htons( port_host );

		ai.ai_family = AF_INET6;
	}

	if( hints ) {
		ai.ai_socktype = hints->ai_socktype;
		ai.ai_protocol = hints->ai_protocol;
	}
	if( node ) {
		strcpy( canonname, node );
		ai.ai_canonname = canonname;
	}
	ai.ai_addrlen = sizeof( P4(sockaddr_storage) );
	ai.ai_addr = addrs.sa;

	*res = &ai;
	return 0;
}

int os::posix::sock::getsockname(int sockfd, P4(sockaddr)* addr, P4(socklen_t)* addrlen)
{
	if( !addr || !addrlen ) {
		return helper::set_errno( EFAULT );
	}
	if( !linux::epoll::isSockFd( sockfd ) ) {
		return helper::set_errno( EBADF );
	}
	linux::epoll::sock_t& sock = linux::epoll::getSock( sockfd );
	TRACE(INFO "getsockname bound={%s:%d}", sock.addr().get_ip_address(), sock.addr().get_port());
	return SocketAddress::trans( sock.addr(), addr, addrlen );
}

int os::posix::sock::getsockopt(int sockfd, int level, int optname, void* optval, P4(socklen_t)* optlen)
{
	if( !optval || !optlen ) {
		return helper::set_errno( EFAULT );
	}
	if( level != SOL_SOCKET ) {
		return helper::set_errno( ENOPROTOOPT );
	}
	if( !linux::epoll::isSockFd( sockfd ) ) {
		return helper::set_errno( EBADF );
	}

	union { void* optval; int* as_int; } ov;
	ov.optval = optval;
	if( optname == SO_ERROR ) {
		int& error = *ov.as_int;
		P4(socklen_t)& size = *optlen;
		error = 0;
		size = sizeof( int );
		return helper::clear_errno();
	}
	else if( optname == SO_TYPE ) {
		int& type = *ov.as_int;
		P4(socklen_t)& size = *optlen;
		type = SOCK_STREAM;
		size = sizeof( int );
		return helper::clear_errno();
	}

	return helper::set_errno( ENOPROTOOPT );
}

uint32_t os::posix::sock::htonl(uint32_t hostlong)
{
	return helper::htonl( hostlong );
}

uint16_t os::posix::sock::htons(uint16_t hostshort)
{
	return helper::htons( hostshort );
}

NOT_IMPL( unsigned int os::posix::sock::if_nametoindex(const char* ifname) );

int os::posix::sock::listen(int sockfd, int backlog)
{
	if( !linux::epoll::isSrvSockFd( sockfd ) ) {
		return helper::set_errno( EOPNOTSUPP );
	}
	nsapi_error_t ret = linux::epoll::getSock( sockfd ).srv().listen( backlog );
	if( ret < 0 ) {
		TRACE(INFO "TCPServer::listen()=%d", ret);
		return helper::set_errno( EBADF );
	}
	return helper::clear_errno();
}

uint16_t os::posix::sock::ntohs(uint16_t netshort)
{
	return helper::ntohs( netshort );
}

P4(ssize_t) os::posix::sock::recvmsg(int sockfd, P4(msghdr)* msg, int flags)
{
	if( !linux::epoll::isUdpSockFd( sockfd ) ) {
		return helper::set_errno( EINVAL );
	}
	TRACE(INFO "msg.msg_namelen={%lu}",		msg->msg_namelen);	// P4(sockaddr) length
	TRACE(INFO "msg.msg_name={%p}",			msg->msg_name);		// P4(sockaddr) *
	TRACE(INFO "msg.msg_iovlen={%d}",		msg->msg_iovlen);
	TRACE(INFO "msg.msg_iov={%p}",			msg->msg_iov);
	TRACE(INFO "msg.msg_controllen={%lu}",	msg->msg_controllen);
	TRACE(INFO "msg.msg_control={%p}",		msg->msg_control);
	TRACE(INFO "msg.msg_flags={%d}",		msg->msg_flags);

	linux::epoll::sock_t& sock = linux::epoll::getSock( sockfd );
	P4(iovec)* iov = msg->msg_iov;
	SocketAddress sa;
	P4(ssize_t) recv = 0;
	for( int x = 0; x < msg->msg_iovlen; ++ x, ++ iov ) {
		TRACE(INFO "iov.iov_len={%u}",	iov->iov_len);
		TRACE(INFO "iov.iov_base={%p}",	iov->iov_base);
		nsapi_size_or_error_t ret = sock.udp().recvfrom( &sa, iov->iov_base, iov->iov_len );
		if( NSAPI_ERROR_WOULD_BLOCK == ret ) {
			return helper::set_errno( EWOULDBLOCK );
		}
		if( ret < 0 ) {
			TRACE(INFO "UDPSocket::recvfrom()=%d", ret);
			return helper::set_errno( EINVAL );
		}
		linux::epoll::poll_.release();	// POLLxx
		recv += ret;
	}

	if( msg->msg_name ) {
		TRACE(INFO "recvmsg from={%s:%d}", sa.get_ip_address(), sa.get_port());
		if( SocketAddress::trans( sa, msg->msg_name, &msg->msg_namelen ) < 0 ) {
			return helper::set_errno( EINVAL );
		}
	}
	return recv;
}

P4(ssize_t) os::posix::sock::sendmsg(int sockfd, const P4(msghdr)* msg, int flags)
{
	if( !linux::epoll::isUdpSockFd( sockfd ) ) {
		return helper::set_errno( EINVAL );
	}

	TRACE(INFO "msg.msg_namelen={%lu}",		msg->msg_namelen);	// P4(sockaddr) length
	TRACE(INFO "msg.msg_name={%p}",			msg->msg_name);		// P4(sockaddr) *
	TRACE(INFO "msg.msg_iovlen={%d}",		msg->msg_iovlen);
	TRACE(INFO "msg.msg_iov={%p}",			msg->msg_iov);
	TRACE(INFO "msg.msg_controllen={%lu}",	msg->msg_controllen);
	TRACE(INFO "msg.msg_control={%p}",		msg->msg_control);

	P4(sockaddr)& ai = *static_cast< P4(sockaddr)* >( msg->msg_name );
	SocketAddress sa = SocketAddress::trans( ai );
	TRACE(INFO "sendmsg to={%s:%d}", sa.get_ip_address(), sa.get_port());

	linux::epoll::sock_t& sock = linux::epoll::getSock( sockfd );
	P4(iovec)* iov = msg->msg_iov;
	P4(ssize_t) sent = 0;
	for( int x = 0; x < msg->msg_iovlen; ++ x, ++ iov ) {
		TRACE(INFO "iov.iov_len={%u}",	iov->iov_len);
		TRACE(INFO "iov.iov_base={%p}",	iov->iov_base);
		sent += sock.udp().sendto( sa, iov->iov_base, iov->iov_len );
	}
	return sent;
}

NOT_IMPL( int os::posix::sock::setsockopt(int sockfd, int level, int optname, const void* optval, P4(socklen_t) optlen) );

int os::posix::sock::shutdown(int sockfd, int how)
{
	if( linux::epoll::isTcpSockFd( sockfd ) ) {
		linux::epoll::sock_t& socket = linux::epoll::getSock( sockfd );
		return socket.shutdown( how );
	}
	return 0;
}

int os::posix::sock::socket(int domain, int type, int protocol)
{
	if( domain != AF_INET ) {
		TRACE(INFO "domain=unknown");
		return helper::set_errno( EINVAL );
	}

	using namespace os::linux::epoll;
	int new_fd = fd_builder_.allocate();
	if( new_fd < 0 ) {
		return helper::set_errno( ENFILE );
	}

	TRACE(INFO "domain=AF_INET");
	if( type == SOCK_STREAM ) {
		TRACE(INFO "type=SOCK_STREAM");
		if( linux::epoll::newSock( new_fd ).asTCP().open() < 0 ) {
			fd_builder_.release( new_fd );
			return helper::set_errno( ENFILE );
		}
	}
	else if( type == SOCK_DGRAM ) {
		TRACE(INFO "type=SOCK_DGRAM");
		if( linux::epoll::newSock( new_fd ).asUDP().open() < 0 ) {
			fd_builder_.release( new_fd );
			return helper::set_errno( ENFILE );
		}
	}
	else {
		TRACE(INFO "type=unknown");
		fd_builder_.release( new_fd );
		return helper::set_errno( EINVAL );
	}

	return new_fd;
}

int os::posix::sys::nanosleep(const P4(timespec)* req, P4(timespec)* rem)
{
	double sec = req->tv_sec + req->tv_nsec / double( 1 * 1000 * 1000 * 1000 );
	wait( sec );
	return helper::clear_errno();
}

NOT_IMPL( int os::posix::sys::pipe(int pipefd[2]) );
NOT_IMPL( long os::posix::sys::sysconf(int name) );


NOT_IMPL( P4(ssize_t) os::linux::preadv(int fd, const P4(iovec)* iov, int iovcnt, P4(off_t) offset) );
NOT_IMPL( P4(ssize_t) os::linux::pwritev(int fd, const P4(iovec)* iov, int iovcnt, P4(off_t) offset) );

int os::linux::epoll::create(int size)
{
	return P4(epoll_create1)( 0 );
}

int os::linux::epoll::create1(int flags)
{	// flags: EPOLL_CLOEXEC=0x00080000
	return newEPollFd( flags );
}

int os::linux::epoll::ctl(int epfd, int op, int fd, P4(poll_event)* event)
{
	if( !isEPollFd( epfd ) ) {
		return helper::set_errno( EBADF );
	}

	poll_internal& pi = getEPollInfo( epfd );
	if( op == POLL_CTL_ADD ) {
		if( pi.find( fd ) != pi.end() ) {
			return helper::set_errno( EEXIST );
		}
		TRACE(INFO "epoll_ctl ADD fd=%d", fd);
		P4(poll_event) ev = *event;
		//ev.events |= POLLHUP;
		//ev.events |= POLLERR;
		pi[ fd ] = ev;	// add
		return helper::clear_errno();
	}
	else if( op == POLL_CTL_MOD ) {
		poll_internal::iterator itr = pi.find( fd );
		if( itr == pi.end() ) {
			return helper::set_errno( ENOENT );
		}
		TRACE(INFO "epoll_ctl MOD fd=%d", fd);
		P4(poll_event) ev = *event;
		//ev.events |= POLLHUP;
		//ev.events |= POLLERR;
		itr->second = ev;	// modify
		return helper::clear_errno();
	}
	else if( op == POLL_CTL_DEL ) {
		poll_internal::iterator itr = pi.find( fd );
		if( itr == pi.end() ) {
			return helper::set_errno( ENOENT );
		}
		TRACE(INFO "epoll_ctl DEL fd=%d", fd);
		pi.erase( itr );	// delete
		return helper::clear_errno();
	}
	return helper::set_errno( EINVAL );
}

int os::linux::epoll::pwait(int epfd, P4(poll_event)* events, int maxevents, int timeout, const P4(sigset_t)* sigmask)
{
	if( !isEPollFd( epfd ) || maxevents <= 0 ) {
		return helper::set_errno( EINVAL );
	}

	for(;;) {
		if( !poll_.wait( timeout ) ) {
			break;	// timeout
		}

		int candidate = 0;

		const poll_internal& pi = getEPollInfo( epfd );
		poll_internal::const_iterator citr = pi.begin();
		for( ; citr != pi.end() && candidate < maxevents; ++ citr ) {
			int evfd = citr->first;
			uint32_t polling_events = citr->second.events;

			uint32_t event_mask = 0;
			if( isEventFd( evfd ) ) {
				eventfd_t counter = getEventFd( evfd );
				if( counter )	event_mask = POLLIN;
				else			event_mask = POLLOUT;
			}
			else if( isSrvSockFd( evfd ) ) {
				if( getSock( evfd ).evt_check() ) {
	TRACE(INFO "epoll_wait socketfd=%d events=0x%08lX", evfd, polling_events);
					event_mask = POLLIN;
				}
			}
			else if( isTcpSockFd( evfd ) ) {
				if( getSock( evfd ).evt_check() ) {
	TRACE(INFO "epoll_wait socketfd=%d events=0x%08lX", evfd, polling_events);
					event_mask = polling_events;
				}
			}
			else if( isUdpSockFd( evfd ) ) {
				if( getSock( evfd ).evt_check() ) {
	TRACE(INFO "epoll_wait socketfd=%d events=0x%08lX", evfd, polling_events);
					event_mask = polling_events;
				}
			}
			else {
	TRACE(INFO "epoll_wait came here evfd=%d", evfd);
				event_mask = POLLHUP;
			}

			if( !( polling_events & event_mask ) ) {
				continue;
			}

			P4(poll_event) ev;
			ev.events = event_mask;
			ev.data.fd = evfd;
			events[ candidate ++ ] = ev;
		}

		if( 0 < candidate ) {
			TRACE(INFO "epoll_wait candidate=%d", candidate);
			for( int i = 0; i < candidate; ++ i ) {
				TRACE(INFO "[%d]={fd:%d, events:0x%08X}", i, events[ i ].data.fd, events[ i ].events);
			}
			return candidate;
		}
	}
	return 0;
}

int os::linux::epoll::wait(int epfd, P4(poll_event)* events, int maxevents, int timeout)
{
	return P4(epoll_pwait)( epfd, events, maxevents, timeout, 0 );
}

int os::linux::epoll::eventfd(unsigned int initval, int flags)
{
	return P4(eventfd2)( initval, flags );
}

int os::linux::epoll::eventfd2(unsigned int initval, int flags)
{	// flags: EFD_CLOEXEC=0x00080000, EFD_NONBLOCK=0x00000800
	return newEventFd( initval );
}

int os::linux::sock::accept4(int fd, P4(sockaddr)* addr, P4(socklen_t)* addr_len, int flags)
{
	if( !linux::epoll::isSrvSockFd( fd ) ) {
		return helper::set_errno( EINVAL );
	}

	using namespace os::linux::epoll;
	int new_fd = fd_builder_.allocate();
	if( new_fd < 0 ) {
		return helper::set_errno( ENFILE );
	}

	linux::epoll::sock_t& server = linux::epoll::getSock( fd );
	linux::epoll::sock_t& client = linux::epoll::newSock( new_fd ).asTCP();
	nsapi_error_t ret = server.srv().accept( &client.tcp(), &client.addr() );
	if( ret == NSAPI_ERROR_WOULD_BLOCK ) {
		fd_builder_.release( new_fd );
		return helper::set_errno( EWOULDBLOCK );
	}
	if( ret < 0 ) {
		fd_builder_.release( new_fd );
		return helper::set_errno( EINVAL );
	}

	TRACE(INFO "accepted %s:%d", client.addr().get_ip_address(), client.addr().get_port());
	if( addr && addr_len ) {
		if( SocketAddress::trans( client.addr(), addr, addr_len ) < 0 ) {
			fd_builder_.release( new_fd );
			return helper::set_errno( EFAULT );
		}
	}

	client.attach();
	return new_fd;
}

NOT_IMPL( int os::linux::sock::socketcall(int call, unsigned long* args) );
NOT_IMPL( int os::linux::sys::ioctl(int fd, unsigned long request, va_list args) );

// see deps/libtuv/src/unix/linux-syscalls.c
#define __NR_accept4		(366)	// syscall(__NR_accept4, fd, addr, addrlen, flags);
#define __NR_epoll_create	(250)	// syscall(__NR_epoll_create, size);
#define __NR_epoll_create1	(357)	// syscall(__NR_epoll_create1, flags);
#define __NR_epoll_ctl		(251)	// syscall(__NR_epoll_ctl, epfd, op, fd, events);
#define __NR_epoll_pwait	(346)	// syscall(__NR_epoll_pwait,
#define __NR_epoll_wait		(252)	// syscall(__NR_epoll_wait, epfd, events, nevents, timeout);
#define __NR_eventfd		(351)	// syscall(__NR_eventfd, count);
#define __NR_eventfd2		(356)	// syscall(__NR_eventfd2, count, flags);
#define __NR_pipe2			(359)	// syscall(__NR_pipe2, pipefd, flags);
#define __NR_preadv			(361)	// syscall(__NR_preadv, fd, iov, iovcnt, (long)offset, (long)(offset >> 32));
#define __NR_pwritev		(362)	// syscall(__NR_pwritev, fd, iov, iovcnt, (long)offset, (long)(offset >> 32));
#define __NR_socketcall		(102)	// syscall(__NR_socketcall, 18 /* SYS_ACCEPT4 */, args);
#define __NR_utimensat		(348)	// syscall(__NR_utimensat, dirfd, path, times, flags);

long os::linux::sys::syscall(long number, va_list args)
{
#define P(type) va_arg(args, type)
	switch(number)
	{
	case __NR_accept4:		return P4(accept4)( P(int), P(P4(sockaddr)*), P(P4(socklen_t)*), P(int) );
	case __NR_epoll_create:	return P4(epoll_create)( P(int) );
	case __NR_epoll_create1:return P4(epoll_create1)( P(int) );
	case __NR_epoll_ctl:	return P4(epoll_ctl)( P(int), P(int), P(int), P(P4(poll_event)*) );
	case __NR_epoll_pwait:	return P4(epoll_pwait)( P(int), P(P4(poll_event)*), P(int), P(int), P(const P4(sigset_t)*) );
	case __NR_epoll_wait:	return P4(epoll_wait)( P(int), P(P4(poll_event)*), P(int), P(int) );
	case __NR_eventfd:		return P4(eventfd)( P(int), P(int) );
	case __NR_eventfd2:		return P4(eventfd2)( P(int), P(int) );
	case __NR_pipe2:		return P4(pipe2)( P(int*), P(int) );
	case __NR_preadv:		return P4(preadv)( P(int), P(const P4(iovec)*), P(int), P(P4(off_t)) );
	case __NR_pwritev:		return P4(pwritev)( P(int), P(const P4(iovec)*), P(int), P(P4(off_t)) );
	case __NR_socketcall:	return P4(socketcall)( P(int), P(unsigned long*) );
	case __NR_utimensat:	return P4(utimensat)( P(int), P(const char*), P(const P4(timespec)*), P(int) );
	}
#undef P
	TRACE(INFO "syscall(unknown (num=%ld), ...)", number);
	return helper::set_errno( ENOSYS );
}

NOT_IMPL( int os::linux::sys::pipe2(int pipefd[2], int flags) );
NOT_IMPL( void* os::linux::sys::dlopen(const char* filename, int flag) );
NOT_IMPL( void* os::linux::sys::dlsym(void* handle, char* symbol) );
NOT_IMPL( int os::linux::sys::dlclose(void* handle) );
NOT_IMPL( char** os::linux::debug::backtrace_symbols(void*const* buffer, int size) );
NOT_IMPL( int os::linux::debug::backtrace(void** buffer, int size) );


void os::overridden::abort()
{
	::abort();
}

int os::overridden::gettimeofday(P4(timeval)* tv, void* tz)
{
	return posix::obsolete::gettimeofday( tv, static_cast<timezone*>( tz ) );
}

P4(ssize_t) os::overridden::read(int fd, void* buf, P4(size_t) count)
{
	if( linux::epoll::isRelatedFd( fd ) ) {
		return linux::epoll::read( fd, buf, count );
	}
	return ::read( fd, buf, count );
}

P4(ssize_t) os::overridden::write(int fd, const void* buf, P4(size_t) count)
{
	if( linux::epoll::isRelatedFd( fd ) ) {
		return linux::epoll::write( fd, buf, count );
	}
	return ::write( fd, buf, count );
}

int os::overridden::open(const char* pathname, va_list args)
{
	return bugfix::open( pathname, args );
}

int os::overridden::close(int fd)
{
	if( linux::epoll::isRelatedFd( fd ) ) {
		return linux::epoll::close( fd );
	}
	return bugfix::close( fd );
}

int os::overridden::unlink(const char* pathname)
{
	return ::remove( pathname );
}

int os::overridden::mkdir(const char* pathname, P4(mode_t) mode)
{
	return ::mkdir( pathname, mode );
}

int os::overridden::rmdir(const char* pathname)
{
	return ::remove( pathname );
}

int os::overridden::stat(const char* pathname, P4(stat_t)* buf)
{
	return bugfix::stat( pathname, buf );
}

int os::overridden::fstat(int fd, P4(stat_t)* buf)
{
	return bugfix::fstat( fd, buf );
}


char* os::nic::enumerate()
{
	return helper::nic.enumerate();
}

int os::nic::ifup(const char* nic, const char* params)
{
	return helper::nic.ifup( nic, params ) ? 0 : -1;
}

int os::nic::ifdown(const char* nic)
{
	return helper::nic.ifdown( nic ) ? 0 : -1;
}

char* os::nic::ifconfig(const char* nic)
{
	return helper::nic.ifconfig( nic );
}

int os::nic::ntpdate(const char* nic, const char* server, int port, int timeout)
{
	return helper::nic.ntpdate( nic, server, port, timeout ) ? 0 : -1;
}


#define NEWLIB_OVERRIDE extern "C"

NEWLIB_OVERRIDE int gettimeofday(struct P4(timeval)* tv, void* tz)
{
	return overridden::gettimeofday( tv, tz );
}


#define LWIP_OVERRIDE extern "C"

#include "lwip/err.h"
LWIP_OVERRIDE err_t tcp_send_fin(struct tcp_pcb* pcb);
LWIP_OVERRIDE err_t __real_tcp_write(struct tcp_pcb* pcb, const void* arg, u16_t len, u8_t apiflags);
LWIP_OVERRIDE err_t __wrap_tcp_write(struct tcp_pcb* pcb, const void* arg, u16_t len, u8_t apiflags)
{
	if( strcmp((char*)arg, NAME_STRINGIZER(VIRTUAL_FIN_PACKET)) == 0 ) {
		return tcp_send_fin(pcb);
	}
	return __real_tcp_write(pcb, arg, len, apiflags);
}

#define MBEDTLS_OVERRIDE extern "C"

MBEDTLS_OVERRIDE int __real_mbedtls_hardware_poll(void* data, unsigned char* output, size_t length, size_t* output_length);
MBEDTLS_OVERRIDE int __wrap_mbedtls_hardware_poll(void* data, unsigned char* output, size_t length, size_t* output_length)
{
#if ENABLE_MODULE_VIDEO==1
	return __real_mbedtls_hardware_poll(data, output, length, output_length);
#else
	if( !output || !output_length ) {
		return -1;
	}

	u16_t* out = reinterpret_cast<u16_t*>( output );
	size_t len = length / sizeof( u16_t );
	for( size_t i=0; i<len; ++i ) {
		*out++ = rand();
	}

	output = reinterpret_cast<u8_t*>( out );
	len = length % sizeof( u16_t );
	for( size_t i=0; i<len; ++i ) {
		*output++ = rand();
	}
	*output_length = length;
	return 0;
#endif
}
