#if defined(__linux__)

#if defined(CROSS)
/* To avoid compile error by differ of return value of read()/write() */
#define _READ_WRITE_RETURN_TYPE _ssize_t
#endif
#include <stdlib.h>
#include <stdint.h>
#include <stdarg.h>
#include <string.h>
#include <errno.h>
#include <unistd.h>
#include <time.h>
#include <signal.h>
#include <dirent.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <sys/stat.h>
#include <sys/mman.h>
#include <sys/select.h>
#include <sys/time.h>
#include <sys/ioctl.h>

#include <nllibc.h>

int _set_errno(int retval)
{
  if ((retval < 0) && (retval > -4096)) {
    errno = -retval;
    retval = -1;
  }
  return retval;
}

struct rusage;

void ___exit(int status);
pid_t __fork(void);
pid_t __wait4(pid_t wpid, int *status, int options, struct rusage *rusage);
int __kill(pid_t pid, int sig);
int __execve(const char *path, char *const argv[], char *const envp[]);

ssize_t __read(int fd, void *buf, size_t nbytes);
ssize_t __write(int fd, const void *buf, size_t nbytes);
int __open(const char *path, int flags, mode_t mode);
int __close(int fd);

int __dup(int oldd);
int __pipe(int fildes[2]);
int __dup2(int oldd, int newd);
int __pipe2(int fildes[2], int flags);

/* Addtional 4th argument for Linux rt_sigaction (sigset_size) */
int __sigaction(int sig, const struct sigaction *act, struct sigaction *oact, int sigset_size);
int __sigprocmask(int how, const sigset_t *set, sigset_t *oset);
struct sigcontext;
int __sigreturn(struct sigcontext *scp);

pid_t __getpid(void);
pid_t __getppid(void);
pid_t __getsid(pid_t pid);
pid_t __setsid(void);
pid_t __getpgid(pid_t pid);
int __setpgid(pid_t pid, pid_t pgrp);
pid_t __getpgrp(void);

int __unlink(const char *path);
int __chdir(const char *path);
int __fchdir(int fd);
int __chmod(const char *path, mode_t mode);
int __chown(const char *path, uid_t owner, gid_t group);
off_t __lseek(int fildes, off_t offset, int whence);
int __access(const char *path, int mode);
void __sync(void);
int __fcntl(int fd, int cmd, int args);
int __fsync(int fd);
int __fchown(int fd, uid_t owner, gid_t group);
int __fchmod(int fd, mode_t mode);
int __rename(const char *from, const char *to);

int __mkdir(const char *path, mode_t mode);
int __rmdir(const char *path);

void *__mmap(void *addr, size_t len, int prot, int flags, int fd, off_t offset);
int __munmap(void *addr, size_t len);

int __truncate(const char *path, off_t length);
int __ftruncate(int fd, off_t length);

int __select(int nfds, fd_set *readfds, fd_set *writefds, fd_set *exceptfds, struct timeval *timeout);
int __pselect(int nfds, fd_set *readfds, fd_set *writefds, fd_set *exceptfds, const struct timespec *timeout, const sigset_t *newsigmask);

int __gettimeofday(struct timeval *tp, struct timezone *tzp);
int __settimeofday(const struct timeval *tp, const struct timezone *tzp);

int __ioctl(int fd, unsigned long request, unsigned long args);
void *__brk(const void *addr);
int __getdents(int fd, char *buf, int nbytes);
int ____getcwd(char *buf, size_t size);

void _exit(int status)
{
  ___exit(status);
  while (1) /* to avoid noreturn warning */
    ;
}

pid_t fork(void)
{
  return _set_errno(__fork());
}

pid_t wait4(pid_t wpid, int *status, int options, struct rusage *rusage)
{
  return _set_errno(__wait4(wpid, status, options, rusage));
}

int kill(pid_t pid, int sig)
{
  return _set_errno(__kill(pid, sig));
}

int execve(const char *path, char *const argv[], char *const envp[])
{
  return _set_errno(__execve(path, argv, envp));
}

ssize_t read(int fd, void *buf, size_t nbytes)
{
  return _set_errno(__read(fd, buf, nbytes));
}

ssize_t write(int fd, const void *buf, size_t nbytes)
{
  return _set_errno(__write(fd, buf, nbytes));
}

static int _va_open(const char *path, int flags, va_list ap)
{
  unsigned long arg;
  arg = va_arg(ap, unsigned long);
  return __open(path, flags, arg);
}

int open(const char *path, int flags, ...)
{
  va_list arg;
  int r;

  va_start(arg, flags);
  r = _va_open(path, flags, arg);
  va_end(arg);

  return _set_errno(r);
}

int close(int fd)
{
  return _set_errno(__close(fd));
}

int dup(int oldd)
{
  return _set_errno(__dup(oldd));
}

int pipe(int fildes[2])
{
  return _set_errno(__pipe(fildes));
}

int dup2(int oldd, int newd)
{
  return _set_errno(__dup2(oldd, newd));
}

int pipe2(int fildes[2], int flags)
{
  return _set_errno(__pipe2(fildes, flags));
}

int sigaction(int sig, const struct sigaction *act, struct sigaction *oact)
{
#ifdef SA_RESTORER
  struct sigaction nact;
  if (!(act->sa_flags & SA_RESTORER)) {
    memcpy(&nact, act, sizeof(nact));
    nact.sa_flags |= SA_RESTORER;
    nact.sa_restorer = (void (*)(void))__sigreturn;
    act = &nact;
  }
#endif
  /* Addtional 4th argument for Linux rt_sigaction (sigset_size) */
  return _set_errno(__sigaction(sig, act, oact, sizeof(sigset_t)));
}

int sigprocmask(int how, const sigset_t *set, sigset_t *oset)
{
  return _set_errno(__sigprocmask(how, set, oset));
}

int sigreturn(struct sigcontext *scp)
{
  return _set_errno(__sigreturn(scp));
}

pid_t getpid(void)
{
  return _set_errno(__getpid());
}

pid_t getppid(void)
{
  return _set_errno(__getppid());
}

pid_t getsid(pid_t pid)
{
  return _set_errno(__getsid(pid));
}

pid_t setsid(void)
{
  return _set_errno(__setsid());
}

pid_t getpgid(pid_t pid)
{
  return _set_errno(__getpgid(pid));
}

int setpgid(pid_t pid, pid_t pgrp)
{
  return _set_errno(__setpgid(pid, pgrp));
}

pid_t getpgrp(void)
{
  return _set_errno(__getpgrp());
}

int unlink(const char *path)
{
  return _set_errno(__unlink(path));
}

int chdir(const char *path)
{
  return _set_errno(__chdir(path));
}

int fchdir(int fd)
{
  return _set_errno(__fchdir(fd));
}

int chmod(const char *path, mode_t mode)
{
  return _set_errno(__chmod(path, mode));
}

int chown(const char *path, uid_t owner, gid_t group)
{
  return _set_errno(__chown(path, owner, group));
}

off_t lseek(int fildes, off_t offset, int whence)
{
  return _set_errno(__lseek(fildes, offset, whence));
}

int access(const char *path, int mode)
{
  return _set_errno(__access(path, mode));
}

void sync(void)
{
  __sync();
}

static int _va_fcntl(int fd, int cmd, va_list ap)
{
  int arg;
  arg = va_arg(ap, int);
  return __fcntl(fd, cmd, arg);
}

int fcntl(int fd, int cmd, ...)
{
  va_list arg;
  int r;

  va_start(arg, cmd);
  r = _va_fcntl(fd, cmd, arg);
  va_end(arg);

  return _set_errno(r);
}

int fsync(int fd)
{
  return _set_errno(__fsync(fd));
}

int fchown(int fd, uid_t owner, gid_t group)
{
  return _set_errno(__fchown(fd, owner, group));
}

int fchmod(int fd, mode_t mode)
{
  return _set_errno(__fchmod(fd, mode));
}

int rename(const char *from, const char *to)
{
  return _set_errno(__rename(from, to));
}

int mkdir(const char *path, mode_t mode)
{
  return _set_errno(__mkdir(path, mode));
}

int rmdir(const char *path)
{
  return _set_errno(__rmdir(path));
}

void *mmap(void *addr, size_t len, int prot, int flags, int fd, off_t offset)
{
  return __mmap(addr, len, prot, flags, fd, offset);
}

int munmap(void *addr, size_t len)
{
  return _set_errno(__munmap(addr, len));
}

int truncate(const char *path, off_t length)
{
  return _set_errno(__truncate(path, length));
}

int ftruncate(int fd, off_t length)
{
  return _set_errno(__ftruncate(fd, length));
}

int select(int nfds, fd_set *readfds, fd_set *writefds, fd_set *exceptfds, struct timeval *timeout)
{
  return _set_errno(__select(nfds, readfds, writefds, exceptfds, timeout));
}

int pselect(int nfds, fd_set *readfds, fd_set *writefds, fd_set *exceptfds, const struct timespec *timeout, const sigset_t *newsigmask)
{
  return _set_errno(__pselect(nfds, readfds, writefds, exceptfds, timeout, newsigmask));
}

int gettimeofday(struct timeval *tp, struct timezone *tzp)
{
  return _set_errno(__gettimeofday(tp, tzp));
}

int settimeofday(const struct timeval *tp, const struct timezone *tzp)
{
  return _set_errno(__settimeofday(tp, tzp));
}

static int _va_ioctl(int fd, unsigned long request, va_list ap)
{
  unsigned long arg;
  arg = va_arg(ap, unsigned long);
  return __ioctl(fd, request, arg);
}

int ioctl(int fd, unsigned long request, ...)
{
  va_list arg;
  int r;

  va_start(arg, request);
  r = _va_ioctl(fd, request, arg);
  va_end(arg);

  return _set_errno(r);
}

int brk(void *addr)
{
  return __brk(addr) ? 0 : -1;
}

void *sbrk(intptr_t incr)
{
  static char *endp = NULL;
  char *oldp, *newp;
  long pagesize = 0x1000;

  if (endp == NULL) {
    endp = __brk(NULL);
    endp = (char *)(((long)endp + pagesize - 1) & ~(pagesize - 1));
    if (brk(endp) < 0)
      goto err;
  }

  oldp = endp;
  newp = endp + incr;
  newp = (char *)(((long)newp + pagesize - 1) & ~(pagesize - 1));

  if (incr != 0) {
    if (brk(newp) < 0)
      goto err;
    endp = newp;
  }
  return oldp;

err:
  return (void *)-1;
}

int getdents(int fd, char *buf, int nbytes)
{
  return _set_errno(__getdents(fd, buf, nbytes));
}

int __getcwd(char *buf, size_t size)
{
  return _set_errno(____getcwd(buf, size));
}
#endif
