// Local configuration
#include "config.h"

// Implementation
#include "Sys.hpp"

// STDC++
#include <string>
#include <fstream>

// POSIX C
#ifdef WIN32
#include <Windows.h>
#else
#include <sys/wait.h>
#include <sys/types.h>
#include <sys/syscall.h>
#endif

// System C
#ifdef __linux__
#include <sys/quota.h>
#include <sys/fsuid.h>
#ifdef HAVE_LIBPROC
#include <proc/readproc.h>
#endif
#endif

// C++ libraries
#include <ui-utilcpp/Text.hpp>
#include <ui-utilcpp/File.hpp>

namespace UI {
namespace Util {
namespace Sys {

#ifdef WIN32
// From MSDN:
// The WSAStartup function must be the first Windows Sockets function called by an application or DLL.
// It allows an application or DLL to specify the version of Windows Sockets required and retrieve details of the specific Windows Sockets implementation.
// The application or DLL can only issue further Windows Sockets functions after successfully calling WSAStartup.
void wsaStartup()
{
	WSADATA wsaData = {};
	int err = ::WSAStartup(MAKEWORD(2,2), &wsaData);
	if (err != 0)
	{
		UI_THROW_ERRNO("WSAStartup failed with error " + tos(err));
	}
	// Confirm that the WinSock DLL supports 2.2.
	// Note that if the DLL supports versions greater than 2.2 in addition to 2.2, it will still return
	// 2.2 in wVersion since that is the version we requested.
	if (LOBYTE(wsaData.wVersion) != 2 || HIBYTE(wsaData.wVersion) != 2)
	{
		::WSACleanup();
		UI_THROW_ERRNO("Could not find a usable version of Winsock.dll");
	}
}
void wsaCleanup()
{
	::WSACleanup();
}
std::string convertPath(std::string const & path)
{
	// initialize result
	CString strResult( path.c_str() );

	// replace slashes with backslashes
	strResult.Replace( "/", "\\" );

	// remove slashes and backslashes (some paths have even 3 slashes at the end)
	while( ( 1 < strResult.GetLength() ) &&
				( '\\' == strResult[strResult.GetLength()-1] ) )
		strResult = strResult.Left( strResult.GetLength() - 1 );

	while( -1 != strResult.Find( "\\\\" ) )
	{
		strResult.Replace( "\\\\", "\\" );
	}

	// return fixed path
	return std::string( (LPCTSTR)strResult );
}
#endif

void * calloc(size_t nmemb, size_t size)
{
	void * result(std::calloc(nmemb, size));
	if (result == 0) { UI_THROW_ERRNO("Error calloc(3)ing " + tos(size) + " bytes: "); }
	return result;
}

void * malloc(size_t size)
{
	void * result(std::malloc(size));
	if (result == 0) { UI_THROW_ERRNO("Error malloc(3)ing " + tos(size) + " bytes: "); }
	return result;
}

void free(void *ptr)
{
	std::free(ptr);
}

int system(char const * s)
{
	int const result(std::system(s));
	if (result < 0) { UI_THROW_ERRNO("Error system(3)ing: " + std::string(s)); }
	return result;
}

char * getenv(char const * name)
{
	char * result(::getenv(name));
	if (result == 0) { UI_THROW("Error getenv(3)ing: No such environment variable \"" + std::string(name) + "\""); }
	return result;
}

void realpath(char const * path, char * resolved_path)
{
	if (::realpath(path, resolved_path) == 0) { UI_THROW_ERRNO("Error resolving realpath(3) for " + std::string(path) + ": "); }
}


FILE * fopen(char const * path, char const * mode)
{
	FILE * result(std::fopen(path, mode));
	if (result == 0) { UI_THROW_ERRNO("Error fopen(3)ing " + std::string(path) + ": "); }
	return result;
}

FILE * freopen(char const * path, char const * mode, FILE * stream)
{
	FILE * result(std::freopen(path, mode, stream));
	if (result == 0) { UI_THROW_ERRNO("Error freopen(3)ing " + std::string(path) + ": "); }
	return result;
}

void fclose(FILE * fp)
{
	if (std::fclose(fp) != 0) { UI_THROW_ERRNO("Error fclosing(3)ing file ptr " + tos(fp) + ": "); }
}

mode_t umask(mode_t mask)
{
	return ::umask(mask);
}

void chdir(char const * path)
{
	if (::chdir(path) != 0) { UI_THROW_ERRNO("Error chdir(2)ing to " + std::string(path) + ": "); }
}

void chown(char const * path, uid_t owner, gid_t group)
{
#ifdef WIN32
	// note SL: do not just throw an exception here because this will cause a lot of problems
	// Windows doesn't have to change any ownership so just ignore this
	//UI_THROW("No chown for WIN32");
#else
	if (::chown(path, owner, group) != 0) { UI_THROW_ERRNO("Error chown(2)ing " + std::string(path) + ": "); }
#endif
}

void chmod(char const * path, mode_t mode)
{
	if (::chmod(path, mode) != 0) { UI_THROW_ERRNO("Error chmod(2)ing " + std::string(path) + ": "); }
}

void stat(char const * file_name, struct stat * buf)
{
#ifdef WIN32
	std::string strPath = convertPath( file_name );
	if(::stat(strPath.c_str(), buf) != 0) { UI_THROW_ERRNO("Error stat(2)ing " + std::string(file_name) + ": "); }
#else
	if(::stat(file_name, buf) != 0) { UI_THROW_ERRNO("Error stat(2)ing " + std::string(file_name) + ": "); }
#endif
}

void fstat(int filedes, struct stat * buf)
{
	if (::fstat(filedes, buf) != 0) { UI_THROW_ERRNO("Error fstat(2)ing fd " + tos(filedes) + ": "); }
}

void lstat(char const * file_name, struct stat * buf)
{
#ifdef WIN32
	// note SL: do not just throw an exception here because this will cause a lot of problems
	//UI_THROW("No lstat for WIN32");
#else
	if (::lstat(file_name, buf) != 0) { UI_THROW_ERRNO("Error lstat(2)ing " + std::string(file_name) + ": "); }
#endif
}

void statvfs(char const * path, struct statvfs * buf)
{
#ifdef WIN32
	// note SL: do not just throw an exception here because this will cause a lot of problems
	//UI_THROW("No statvfs for WIN32");
#else
	if (::statvfs(path, buf) != 0) { UI_THROW_ERRNO("Error statvfs(2)ing " + std::string(path) + ": "); }
#endif
}


void unlink(char const * pathname)
{
	if (::unlink(pathname) != 0) { UI_THROW_ERRNO("Error unlink(2)ing " + std::string(pathname) + ": "); }
}

void remove(char const * pathname)
{
	if (::remove(pathname) != 0) { UI_THROW_ERRNO("Error remove(3)ing " + std::string(pathname) + ": "); }
}

void rename(char const * oldpath, char const * newpath)
{
#ifdef WIN32 
	if (fileExists(newpath))
	{
		if (::remove(newpath) == -1) {UI_THROW_ERRNO("Error removing " + std::string(newpath) + ": ");}
	}
#endif

	if (::rename(oldpath, newpath) != 0) { UI_THROW_ERRNO("Error rename(2)ing " + std::string(oldpath) + "->" + std::string(newpath) + ": "); }
}

int open(char const * pathname, int flags)
{
	int const result(::open(pathname, flags));
	if (result < 0) { UI_THROW_ERRNO("Error open(2)ing " + std::string(pathname) + ": "); }
	return result;
}

int open(char const * pathname, int flags, mode_t mode)
{
	int const result(::open(pathname, flags, mode));
	if (result < 0) { UI_THROW_ERRNO("Error open(2)ing " + std::string(pathname) + " with mode " + tos(mode) + ": "); }
	return result;
}

void close(int fd)
{
	if (::close(fd) != 0) { UI_THROW_ERRNO("Error close(2)ing fd " + tos(fd) + ": "); }
}

void mkdir(char const * pathname, mode_t mode)
{
#ifdef WIN32
	if (_mkdir(pathname) != 0) { UI_THROW_ERRNO("Cannot mkdir(2) " + std::string(pathname) + " w/ mode " + tos(mode) + ": "); }
#else
	if (::mkdir(pathname, mode) != 0) { UI_THROW_ERRNO("Cannot mkdir(2) " + std::string(pathname) + " w/ mode " + tos(mode) + ": "); }
#endif
}

void rmdir(char const * pathname)
{
	if (::rmdir(pathname) != 0) { UI_THROW_ERRNO("Cannot rmdir(2) " + std::string(pathname) + ": "); }
}

int dup(int oldfd)
{
	int const result(::dup(oldfd));
	if (result < 0) { UI_THROW_ERRNO("Error dup(2)ing oldfd " + tos(oldfd) + ": "); }
	return result;
}

int dup2(int oldfd, int newfd)
{
	int const result(::dup2(oldfd, newfd));
	if (result < 0) { UI_THROW_ERRNO("Error dup2(2)ing oldfd " + tos(oldfd) + ": "); }
	return result;
}

int fcntl(int fd, int cmd, long arg)
{
#ifdef WIN32
	// note SL: do not just throw an exception here because this will cause a lot of problems
	//UI_THROW("No fcntl for WIN32");
	return -1;
#else
	int const result(::fcntl(fd, cmd, arg));
	if (result == -1) { UI_THROW_ERRNO("Error fcntl(2)ing cmd " + tos(cmd) + " on fd " + tos(fd) + ": "); }
	return result;
#endif
}

int fcntl(int fd, int cmd, struct flock * lock)
{
#ifdef WIN32
	// note SL: do not just throw an exception here because this will cause a lot of problems
	//UI_THROW("No fcntl for WIN32");
	return -1;
#else
	int const result(::fcntl(fd, cmd, lock));
	if (result == -1) { UI_THROW_ERRNO("Error fcntl(2)ing cmd " + tos(cmd) + " on fd " + tos(fd) + ": "); }
	return result;
#endif
}

int flock(int fd, int operation)
{
#ifdef WIN32
	UI_THROW("No flock(2) for WIN32");
	return -1;
#else
	int const result(::flock(fd, operation));
	if (result != 0) { UI_THROW_ERRNO("Error flock(2)ing operation " + tos(operation) + " on fd " + tos(fd) + ": "); }
	return result;
#endif
}

struct passwd * getpwnam(char const * name)
{
#ifdef WIN32
	// note SL: do not just throw an exception here because this will cause a lot of problems
	//UI_THROW("No getpwnam(3) for WIN32");
	return NULL;
#else
	struct passwd * result(::getpwnam(name));
	if (!result) { UI_THROW_ERRNO("Cannot getpwnam(3) for \"" + std::string(name) + "\": "); }
	return result;
#endif
}

struct group * getgrnam(char const * name)
{
#ifdef WIN32
	// note SL: do not just throw an exception here because this will cause a lot of problems
	//UI_THROW("No getgrnam(3) for WIN32");
	return NULL;
#else
	struct group * result(::getgrnam(name));
	if (!result) { UI_THROW_ERRNO("Cannot getgrnam(3) for \"" + std::string(name) + "\": "); }
	return result;
#endif
}

ssize_t write(int fd, void const * buf, size_t count)
{
	int const result(::write(fd, buf, count));
	if (result < 0) { UI_THROW_ERRNO("Error write(2)ing to fd " + tos(fd) + ": "); }
	return result;
}

ssize_t read(int fd, void * buf, size_t count)
{
	int const result(::read(fd, buf, count));
	if (result < 0) { UI_THROW_ERRNO("Error read(2)ing from fd " + tos(fd) + ": "); }
	return result;
}

#ifndef WIN32
pid_t fork(void)
{
	pid_t const result(::fork());
	if (result < 0) { UI_THROW_ERRNO("Error fork(2)ing: "); }
	return result;
}
#endif

pid_t getpid()
{
	return ::getpid();
}

pid_t gettid()
{
#ifdef __linux__
	return ::syscall(SYS_gettid);
#else
	return ::getpid();
#endif
}

pid_t getppid()
{
#ifdef WIN32
	return ::_getpid();
#else
	return ::getppid();
#endif
}

pid_t getpgid(pid_t pid)
{
#ifdef WIN32
	return pid; // Does not seem to be s.th. like parent group
#else
	pid_t const result(::getpgid(pid));
	if (result < 0) { UI_THROW_ERRNO("Cannot getpgid(2) on " + tos(pid) + ": "); }
	return result;
#endif
}

#ifndef WIN32
pid_t waitpid(pid_t pid, int * status, int options)
{
	pid_t const result(::waitpid(pid, status, options));
	if (result < 0) { UI_THROW_ERRNO("Error waitpid(2)ing on " + tos(pid) + ": "); }
	return result;
}
#endif

pid_t setsid()
{
#if WIN32
	return 0;
#else
	pid_t const result(::setsid());
	if (result < 0) { UI_THROW_ERRNO("Error setsid(2)ing: "); }
	return result;
#endif
}

#ifndef WIN32
uid_t getuid() { return ::getuid(); }
gid_t getgid() { return ::getgid(); }
uid_t geteuid() { return ::geteuid(); }
gid_t getegid() { return ::getegid(); }

void seteuid(uid_t euid)
{
	if (::seteuid(euid) < 0) { UI_THROW_ERRNO("Cannot seteuid(2) to " + tos(euid) + ": "); }
}

void setegid(gid_t egid)
{
	if (::setegid(egid) < 0) { UI_THROW_ERRNO("Cannot setegid(2) to " + tos(egid) + ": "); }
}

void setuid(uid_t uid)
{
	if (::setuid(uid) < 0) { UI_THROW_ERRNO("Cannot setuid(2) to " + tos(uid) + ": "); }
}

void setgid(gid_t gid)
{
	if (::setgid(gid) < 0) { UI_THROW_ERRNO("Cannot setgid(2) to " + tos(gid) + ": "); }
}

#ifdef HAVE_LIBPROC
uid_t getfsuid(pid_t const pid)
{
	proc_t p;
	if (::get_proc_stats(pid, &p) != 0)
	{
		return p.fuid;
	}
	UI_THROW("Cannot getfsuid() [libproc] for pid=" + tos(pid));
}

gid_t getfsgid(pid_t const pid)
{
	proc_t p;
	if (::get_proc_stats(pid, &p) != 0)
	{
		return p.fgid;
	}
	UI_THROW("Cannot getfsgid() [libproc] for pid=" + tos(pid));
}

#else

uid_t fsidFromProcfs(pid_t const pid, std::string const & lineId)
{
	try
	{
		// Get path in proc filesystem.
		std::stringstream statPath;
		statPath << "/proc/" << pid << "/status";

		// Open status file
		std::ifstream statFile;
		statFile.exceptions(std::ios::eofbit|std::ios::failbit|std::ios::badbit);
		statFile.open(statPath.str().c_str());

		// Read status file line by line until Uid: line is found
		std::string line;
		while (std::getline(statFile, line))
		{
			if (line.compare(0, 4, lineId + ":") == 0)
			{
				// Tokenize tab-separated line: 'Uid:	1000	1000	1000	1000'
				StrVec tok(strtok(line, "\t"));
				return ato<uid_t>(tok.at(4));
			}
		}
	}
	catch (std::exception const & e)
	{
		UI_THROW("Cannot parse fsid \"" + lineId + "\" from procfs for pid=" + tos(pid) + ": " + e.what());
	}
	UI_THROW("Cannot parse fsid \"" + lineId + "\" from procfs for pid=" + tos(pid));
}

uid_t getfsuid(pid_t const pid)
{
	return fsidFromProcfs(pid, "Uid");
}

gid_t getfsgid(pid_t const pid)
{
	return fsidFromProcfs(pid, "Gid");
}
#endif

void setfsuid(uid_t fsuid)
{
	// No real error handling for setfsuid, so we need an extra check
	::setfsuid(fsuid);
	if (fsuid != getfsuid())
	{
		UI_THROW("Cannot setfsuid(2) to " + tos(fsuid) + ": Still " + tos(getfsuid()));
	}
}

void setfsgid(gid_t fsgid)
{
	// No real error handling for setfsgid, so we need an extra check
	::setfsgid(fsgid);
	if (fsgid != getfsgid())
	{
		UI_THROW("Cannot setfsgid(2) to " + tos(fsgid) + ": Still " + tos(getfsgid()));
	}
}

#else
// dummy implementation
uid_t getuid() { return ::getuid(); }
gid_t getgid() { return ::getuid(); }
uid_t geteuid() { return ::getuid(); }
gid_t getegid() { return ::getuid(); }
uid_t getfsuid(pid_t const) { return 0; }
void seteuid(uid_t) {}
void setegid(gid_t) {}
void setuid(uid_t) {}
void setgid(gid_t) {}
void setfsuid(uid_t fsuid) {}
void setfsgid(gid_t fsgid) {}
#endif

void getrlimit(int resource, struct rlimit * rlim)
{
#ifdef WIN32
	// note SL: do not just throw an exception here because this will cause a lot of problems
	//UI_THROW("No getrlimit for WIN32");
#else
	if (::getrlimit(resource, rlim) == -1) { UI_THROW_ERRNO("Cannot getrlimit(2) from " + tos(resource) + ": "); }
#endif
}

void getrusage(int who, struct rusage * usage)
{
#ifdef WIN32
	// note SL: do not just throw an exception here because this will cause a lot of problems
	//UI_THROW("No getrusage for WIN32");
#else
	if (::getrusage(who, usage) == -1) { UI_THROW_ERRNO("Cannot getrusage(2) from " + tos(who) + ": "); }
#endif
}

void setrlimit(int resource, struct rlimit const * rlim)
{
#ifdef WIN32
	// note SL: do not just throw an exception here because this will cause a lot of problems
	//UI_THROW("No setrlimit for WIN32");
#else
	if (::setrlimit(resource, rlim) == -1) { UI_THROW_ERRNO("Cannot setrlimit(2) for " + tos(resource) + ": "); }
#endif
}


unsigned int sleep(unsigned int seconds)
{
#ifdef WIN32
	Sleep(DWORD(seconds * 1000));
	return 0;
#else
	return ::sleep(seconds);
#endif
}


int socket(int domain, int type, int protocol)
{
	int const result(::socket(domain, type, protocol));
	if (result < 0) { UI_THROW_ERRNO("Error on socket(2): "); }
	return result;
}

void getaddrinfo(const char *node, const char *service, const struct addrinfo *hints, struct addrinfo **res)
{
	int const r(::getaddrinfo(node, service, hints, res));
	if (r != 0) { UI_THROW(std::string("Cannot getaddrinfo(3) for '") + node + ":" + service + "': " + ::gai_strerror(r)); }
}

void getnameinfo(const struct sockaddr * sa, socklen_t salen, char * host, size_t hostlen, char * serv, size_t servlen, int flags)
{
	int const res(::getnameinfo(sa, salen, host, hostlen, serv, servlen, flags));
	if (res != 0) { UI_THROW(std::string("Cannot getnameinfo(3): ") + ::gai_strerror(res)); }
}

std::string getnameinfo(const struct sockaddr * sa, socklen_t salen)
{
	char host[255];
	char service[80];
	Sys::getnameinfo(sa, salen, host, 254, service, 79, 0);
	return std::string(host) + ":" + service;
}

void getpeername(int s, struct sockaddr * name, socklen_t * namelen)
{
	if (::getpeername(s, name, namelen) != 0) { UI_THROW_ERRNO("Cannot getpeername(2) from socket fd " + tos(s) + ": "); }
}

void getsockname(int s, struct sockaddr * name, socklen_t * namelen)
{
	if (::getsockname(s, name, namelen) != 0) { UI_THROW_ERRNO("Cannot getsockname(2) from socket fd " + tos(s) + ": "); }
}

void setsockopt(int s, int level, int optname, void const * optval, socklen_t optlen)
{
#ifdef WIN32
	char const * my_optval((char const *)optval);
#else
	void const * my_optval(optval);
#endif
	if (::setsockopt(s, level, optname, my_optval, optlen) != 0) { UI_THROW_ERRNO("Cannot setsockopt(2) on socket fd " + tos(s) + ": "); }
}

void setsockopt_to(int s, int level, int optname, struct timeval const & tv)
{
#ifdef WIN32
	//WinSock wants the timeout in milliseconds
	int const millis(tv.tv_sec * 1000 + tv.tv_usec / 1000);
	Sys::setsockopt(s, level, optname, (const char*)&millis, sizeof(millis));
#else
	Sys::setsockopt(s, level, optname, &tv, sizeof(tv));
#endif
}


ssize_t recv(int s, void * buf, size_t len, int flags)
{
#ifdef WIN32
	ssize_t const result(::recv(s, (char*)buf, len, flags));
#else
	ssize_t const result(::recv(s, buf, len, flags));
#endif
	if (result < 0) { UI_THROW_ERRNO("Error recv(2)ing from socket fd " + tos(s) + ": "); }
	return result;
}

ssize_t send(int s, void const * buf, size_t len, int flags)
{
#ifdef WIN32
	ssize_t const result(::send(s, (char*)buf, len, flags));
#else
	ssize_t const result(::send(s, buf, len, flags));
#endif
	if (result < 0) { UI_THROW_ERRNO("Error send(2)ing to socket fd " + tos(s) + ": "); }
	return result;
}

void listen(int s, int backlog)
{
	if (::listen(s, backlog) != 0) { UI_THROW_ERRNO("Cannot listen(2) on socket fd " + tos(s) + ": "); }
}

void bind(int sockfd, struct sockaddr * my_addr, socklen_t addrlen)
{
	if (::bind(sockfd, my_addr, addrlen) != 0)
	{
		UI_THROW_ERRNO("Cannot bind(2) socket fd '" + tos(sockfd) + "' to '" + Sys::getnameinfo(my_addr, addrlen) + "': ");
	}
}

void connect(int sockfd, const struct sockaddr * serv_addr, socklen_t addrlen)
{
	if (::connect(sockfd, serv_addr, addrlen) != 0)
	{
		UI_THROW_ERRNO("Cannot connect(2) socket fd '" + tos(sockfd) + "' to '" + Sys::getnameinfo(serv_addr, addrlen) + "': ");
	}
}

int select(int n, fd_set * readfds, fd_set * writefds, fd_set * exceptfds, struct timeval * timeout)
{
	int const result(::select(n, readfds, writefds, exceptfds, timeout));
	if (result < 0) { UI_THROW_ERRNO("Error on select(2): "); }
	return result;
}

void socketpair(int d, int type, int protocol, int sv[2])
{
#ifdef WIN32
	// note SL: do not just throw an exception here because this will cause a lot of problems
	//UI_THROW("WIN32 has no socketpair call");
#else
	if (::socketpair(d, type, protocol, sv) != 0) { UI_THROW_ERRNO("Cannot create socketpair(2): "); }
#endif
}


void gettimeofday(struct timeval * tv, struct timezone * tz)
{
	if (::gettimeofday(tv, tz) < 0) { UI_THROW_ERRNO("Cannot gettimeofday(2): "); }
}

void settimeofday(struct timeval const * tv , struct timezone const * tz)
{
#ifdef WIN32
	// note SL: do not just throw an exception here because this will cause a lot of problems
	//UI_THROW("WIN32 has no settimeofday call");
#else
	if (::settimeofday(tv, tz) < 0) { UI_THROW_ERRNO("Cannot settimeofday(2): "); }
#endif
}

iconv_t iconv_open(char const * tocode, char const * fromcode)
{
	iconv_t const result(::iconv_open(tocode, fromcode));
	if (result == (iconv_t) -1) { UI_THROW_ERRNO("Error iconv_open(3)ing: " + std::string(tocode) + "<-" + std::string(fromcode)); }
	return result;
}

void iconv_close(iconv_t cd)
{
	if (::iconv_close(cd) < 0) { UI_THROW_ERRNO("Cannot iconv_close(3) iconv_t " + tos(cd)); }
}

void quotactl(int cmd, char const * special, int id, caddr_t addr)
{
#ifdef WIN32
	// note SL: do not just throw an exception here because this will cause a lot of problems
	//UI_THROW("No quotactl for WIN32");
#else
	if (::quotactl(cmd, special, id, addr) < 0) { UI_THROW_ERRNO("quotactl(2) failed for " + tos(id) + " on " + special + ": "); }
#endif
}

size_t confstr(int name, char * buf, size_t len)
{
#ifdef WIN32
	return (size_t) 0;
#else
	return ::confstr(name, buf, len);
#endif
}

std::string getconf(int id)
{
	size_t n(confstr(id, 0, (size_t)0));
	if (n == 0)
	{
		return EmptyString_;
	}
	else
	{
		std::vector<char> result(n);
		confstr(id, &result[0], n);
		return &result[0];
	}
}

// POSIX1e Capapilities
#ifdef __linux__
cap_t cap_init(void)
{
	cap_t c(::cap_init());
	if (c == 0) { UI_THROW_ERRNO("cap_init(3) failed: "); }
	return c;
}

void cap_free(void * obj)
{
	if (::cap_free(obj) != 0) { UI_THROW_ERRNO("cap_free(3) failed: "); }
}

cap_t cap_dup(cap_t c)
{
	cap_t cDup(::cap_dup(c));
	if (cDup == 0) { UI_THROW_ERRNO("cap_dup(3) failed: "); }
	return cDup;
}

cap_t cap_get_proc(void)
{
	cap_t c(::cap_get_proc());
	if (c == 0) { UI_THROW_ERRNO("cap_get_proc(3) failed: "); }
	return c;
}

void cap_set_proc(cap_t c)
{
	if (::cap_set_proc(c) != 0) { UI_THROW_ERRNO("cap_set_proc(3) failed: "); }
}

void cap_clear(cap_t c)
{
	if (::cap_clear(c) != 0) { UI_THROW_ERRNO("cap_clear(3) failed: "); }
}

#ifdef HAVE_CAP_CLEAR_FLAG
void cap_clear_flag(cap_t c, cap_flag_t f)
{
	if (::cap_clear_flag(c, f) != 0) { UI_THROW_ERRNO("cap_clear_flag(3) failed: "); }
}
#endif

void cap_get_flag(cap_t c, cap_value_t v, cap_flag_t f, cap_flag_value_t *vp)
{
	if (::cap_get_flag(c, v, f, vp) != 0) { UI_THROW_ERRNO("cap_clear_flag(3) failed: "); }
}

void cap_set_flag(cap_t c, cap_flag_t f, int n, const cap_value_t * va, cap_flag_value_t v)
{
	// @note: (cap_value_t *) non-const cast is compat for libcap1 only
	if (::cap_set_flag(c, f, n, (cap_value_t *) va, v) != 0) { UI_THROW_ERRNO("cap_set_flag(3) failed: "); }
}

#ifdef HAVE_CAP_COMPARE
int cap_compare(cap_t c1, cap_t c2)
{
	int result(::cap_compare(c1, c2));
	if (result != 0) { UI_THROW_ERRNO("cap_compare(3) failed: "); }
	return result;
}
#endif

cap_t cap_from_text(char const * s)
{
	cap_t c(::cap_from_text(s));
	if (c == 0) { UI_THROW_ERRNO("cap_from_text(3) failed: "); }
	return c;
}

char * cap_to_text(cap_t c, ssize_t * l)
{
	char * result(::cap_to_text(c, l));
	if (result == 0) { UI_THROW_ERRNO("cap_to_text(3) failed: "); }
	return result;
}

int prctl(int o, unsigned long arg2, unsigned long arg3, unsigned long arg4, unsigned long arg5)
{
	int result(::prctl(o, arg2, arg3, arg4, arg5));
	if (result == -1) { UI_THROW_ERRNO("prctl(2) failed: "); }
	return result;
}
#endif

}}}
