/*-*- mode:c;indent-tabs-mode:nil;c-basic-offset:2;tab-width:8;coding:utf-8 -*-â
âvi: set net ft=c ts=2 sts=2 sw=2 fenc=utf-8                                :viâ
ââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââ¡
â Copyright 2022 Justine Alexandra Roberts Tunney                              â
â                                                                              â
â Permission to use, copy, modify, and/or distribute this software for         â
â any purpose with or without fee is hereby granted, provided that the         â
â above copyright notice and this permission notice appear in all copies.      â
â                                                                              â
â THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL                â
â WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED                â
â WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE             â
â AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL         â
â DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR        â
â PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER               â
â TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR             â
â PERFORMANCE OF THIS SOFTWARE.                                                â
ââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââ*/
#include "libc/errno.h"
#include "libc/str/str.h"
#include "libc/sysv/consts/af.h"
#include "libc/sysv/consts/at.h"
#include "libc/sysv/consts/clock.h"
#include "libc/sysv/consts/f.h"
#include "libc/sysv/consts/fd.h"
#include "libc/sysv/consts/ip.h"
#include "libc/sysv/consts/ipproto.h"
#include "libc/sysv/consts/lock.h"
#include "libc/sysv/consts/madv.h"
#include "libc/sysv/consts/map.h"
#include "libc/sysv/consts/msync.h"
#include "libc/sysv/consts/o.h"
#include "libc/sysv/consts/ok.h"
#include "libc/sysv/consts/rusage.h"
#include "libc/sysv/consts/sicode.h"
#include "libc/sysv/consts/sig.h"
#include "libc/sysv/consts/so.h"
#include "libc/sysv/consts/sock.h"
#include "libc/sysv/consts/sol.h"
#include "libc/sysv/consts/tcp.h"
#include "libc/sysv/consts/termios.h"
#include "libc/sysv/consts/w.h"
#include "libc/sysv/errfuns.h"
#include "tool/build/lib/case.h"
#include "tool/build/lib/endian.h"
#include "tool/build/lib/xlat.h"

#define XLAT(x, y) CASE(x, return y)

int XlatSignal(int x) {
  switch (x) {
    XLAT(1, SIGHUP);
    XLAT(2, SIGINT);
    XLAT(3, SIGQUIT);
    XLAT(4, SIGILL);
    XLAT(5, SIGTRAP);
    XLAT(6, SIGABRT);
    XLAT(7, SIGBUS);
    XLAT(8, SIGFPE);
    XLAT(9, SIGKILL);
    XLAT(10, SIGUSR1);
    XLAT(11, SIGSEGV);
    XLAT(12, SIGUSR2);
    XLAT(13, SIGPIPE);
    XLAT(14, SIGALRM);
    XLAT(15, SIGTERM);
    XLAT(17, SIGCHLD);
    XLAT(18, SIGCONT);
    XLAT(21, SIGTTIN);
    XLAT(22, SIGTTOU);
    XLAT(24, SIGXCPU);
    XLAT(25, SIGXFSZ);
    XLAT(26, SIGVTALRM);
    XLAT(27, SIGPROF);
    XLAT(28, SIGWINCH);
    XLAT(29, SIGIO);
    XLAT(19, SIGSTOP);
    XLAT(31, SIGSYS);
    XLAT(20, SIGTSTP);
    XLAT(23, SIGURG);
    default:
      return einval();
  }
}

int UnXlatSignal(int x) {
  if (x == SIGHUP) return 1;
  if (x == SIGINT) return 2;
  if (x == SIGQUIT) return 3;
  if (x == SIGILL) return 4;
  if (x == SIGTRAP) return 5;
  if (x == SIGABRT) return 6;
  if (x == SIGBUS) return 7;
  if (x == SIGFPE) return 8;
  if (x == SIGKILL) return 9;
  if (x == SIGUSR1) return 10;
  if (x == SIGSEGV) return 11;
  if (x == SIGUSR2) return 12;
  if (x == SIGPIPE) return 13;
  if (x == SIGALRM) return 14;
  if (x == SIGTERM) return 15;
  if (x == SIGCHLD) return 17;
  if (x == SIGCONT) return 18;
  if (x == SIGTTIN) return 21;
  if (x == SIGTTOU) return 22;
  if (x == SIGXCPU) return 24;
  if (x == SIGXFSZ) return 25;
  if (x == SIGVTALRM) return 26;
  if (x == SIGPROF) return 27;
  if (x == SIGWINCH) return 28;
  if (x == SIGIO) return 29;
  if (x == SIGSTOP) return 19;
  if (x == SIGSYS) return 31;
  if (x == SIGTSTP) return 20;
  if (x == SIGURG) return 23;
  return 15;
}

int UnXlatSicode(int sig, int code) {
  if (code == SI_USER) return 0;
  if (code == SI_QUEUE) return -1;
  if (code == SI_TIMER) return -2;
  if (code == SI_TKILL) return -6;
  if (code == SI_MESGQ) return -3;
  if (code == SI_ASYNCIO) return -4;
  if (code == SI_ASYNCNL) return -60;
  if (code == SI_KERNEL) return 0x80;
  if (sig == SIGCHLD) {
    if (code == CLD_EXITED) return 1;
    if (code == CLD_KILLED) return 2;
    if (code == CLD_DUMPED) return 3;
    if (code == CLD_TRAPPED) return 4;
    if (code == CLD_STOPPED) return 5;
    if (code == CLD_CONTINUED) return 6;
    return -1;
  }
  if (sig == SIGTRAP) {
    if (code == TRAP_BRKPT) return 1;
    if (code == TRAP_TRACE) return 2;
    return -1;
  }
  if (sig == SIGSEGV) {
    if (code == SEGV_MAPERR) return 1;
    if (code == SEGV_ACCERR) return 2;
    return -1;
  }
  if (sig == SIGFPE) {
    if (code == FPE_INTDIV) return 1;
    if (code == FPE_INTOVF) return 2;
    if (code == FPE_FLTDIV) return 3;
    if (code == FPE_FLTOVF) return 4;
    if (code == FPE_FLTUND) return 5;
    if (code == FPE_FLTRES) return 6;
    if (code == FPE_FLTINV) return 7;
    if (code == FPE_FLTSUB) return 8;
    return -1;
  }
  if (sig == SIGILL) {
    if (code == ILL_ILLOPC) return 1;
    if (code == ILL_ILLOPN) return 2;
    if (code == ILL_ILLADR) return 3;
    if (code == ILL_ILLTRP) return 4;
    if (code == ILL_PRVOPC) return 5;
    if (code == ILL_PRVREG) return 6;
    if (code == ILL_COPROC) return 7;
    if (code == ILL_BADSTK) return 8;
    return -1;
  }
  if (sig == SIGBUS) {
    if (code == BUS_ADRALN) return 1;
    if (code == BUS_ADRERR) return 2;
    if (code == BUS_OBJERR) return 3;
    if (code == BUS_MCEERR_AR) return 4;
    if (code == BUS_MCEERR_AO) return 5;
    return -1;
  }
  if (sig == SIGIO) {
    if (code == POLL_IN) return 1;
    if (code == POLL_OUT) return 2;
    if (code == POLL_MSG) return 3;
    if (code == POLL_ERR) return 4;
    if (code == POLL_PRI) return 5;
    if (code == POLL_HUP) return 6;
    return -1;
  }
  return -1;
}

int XlatSig(int x) {
  switch (x) {
    XLAT(0, SIG_BLOCK);
    XLAT(1, SIG_UNBLOCK);
    XLAT(2, SIG_SETMASK);
    default:
      return einval();
  }
}

int XlatRusage(int x) {
  switch (x) {
    XLAT(0, RUSAGE_SELF);
    XLAT(-1, RUSAGE_CHILDREN);
    XLAT(1, RUSAGE_THREAD);
    default:
      return einval();
  }
}

int XlatSocketFamily(int x) {
  switch (x) {
    XLAT(0, AF_UNSPEC);
    XLAT(1, AF_UNIX);
    XLAT(2, AF_INET);
    default:
      errno = EPFNOSUPPORT;
      return -1;
  }
}

int UnXlatSocketFamily(int x) {
  switch (x) {
    XLAT(AF_UNSPEC, 0);
    XLAT(AF_UNIX, 1);
    XLAT(AF_INET, 2);
    default:
      return x;
  }
}

int XlatSocketType(int x) {
  switch (x) {
    XLAT(1, SOCK_STREAM);
    XLAT(2, SOCK_DGRAM);
    default:
      return einval();
  }
}

int XlatSocketFlags(int flags) {
  unsigned res = 0;
  if (flags & 0x080000) res |= SOCK_CLOEXEC;
  if (flags & 0x000800) res |= SOCK_NONBLOCK;
  return res;
}

int XlatSocketProtocol(int x) {
  switch (x) {
    XLAT(0, 0);
    XLAT(6, IPPROTO_TCP);
    XLAT(17, IPPROTO_UDP);
    default:
      return einval();
  }
}

int XlatSocketLevel(int level) {
  switch (level) {
    XLAT(0, SOL_IP);
    XLAT(1, SOL_SOCKET);
    XLAT(6, SOL_TCP);
    XLAT(17, SOL_UDP);
    default:
      return einval();
  }
}

int XlatSocketOptname(int level, int optname) {
  if (level == SOL_SOCKET) {
    switch (optname) {
      XLAT(1, SO_DEBUG);
      XLAT(2, SO_REUSEADDR);
      XLAT(3, SO_TYPE);
      XLAT(4, SO_ERROR);
      XLAT(5, SO_DONTROUTE);
      XLAT(6, SO_BROADCAST);
      XLAT(7, SO_SNDBUF);
      XLAT(8, SO_RCVBUF);
      XLAT(9, SO_KEEPALIVE);
      XLAT(13, SO_LINGER);
      XLAT(15, SO_REUSEPORT);
      XLAT(18, SO_RCVLOWAT);
      XLAT(19, SO_SNDLOWAT);
      XLAT(30, SO_ACCEPTCONN);
      default:
        return einval();
    }
  }
  if (level == SOL_TCP) {
    switch (optname) {
      XLAT(1, TCP_NODELAY);
      XLAT(2, TCP_MAXSEG);
#if defined(TCP_CORK)
      XLAT(3, TCP_CORK);
#elif defined(TCP_NOPUSH)
      XLAT(3, TCP_NOPUSH);
#endif
#ifdef TCP_KEEPIDLE
      XLAT(4, TCP_KEEPIDLE);
#endif
#ifdef TCP_KEEPINTVL
      XLAT(5, TCP_KEEPINTVL);
#endif
#ifdef TCP_KEEPCNT
      XLAT(6, TCP_KEEPCNT);
#endif
#ifdef TCP_SYNCNT
      XLAT(7, TCP_SYNCNT);
#endif
#ifdef TCP_WINDOW_CLAMP
      XLAT(10, TCP_WINDOW_CLAMP);
#endif
#ifdef TCP_FASTOPEN
      XLAT(23, TCP_FASTOPEN);
#endif
#ifdef TCP_QUICKACK
      XLAT(12, TCP_QUICKACK);
#endif
#ifdef TCP_NOTSENT_LOWAT
      XLAT(25, TCP_NOTSENT_LOWAT);
#endif
#ifdef TCP_SAVE_SYN
      XLAT(27, TCP_SAVE_SYN);
#endif
#ifdef TCP_FASTOPEN_CONNECT
      XLAT(30, TCP_FASTOPEN_CONNECT);
#endif
      default:
        return einval();
    }
  }
  if (level == SOL_IP) {
    switch (optname) {
      XLAT(1, IP_TOS);
      XLAT(2, IP_TTL);
      XLAT(3, IP_HDRINCL);
      XLAT(14, IP_MTU);
      default:
        return einval();
    }
  }
  return einval();
}

int XlatAccess(int x) {
  int r = F_OK;
  if (x & 1) r |= X_OK;
  if (x & 2) r |= W_OK;
  if (x & 4) r |= R_OK;
  return r;
}

int XlatLock(int x) {
  int r = 0;
  if (x & 1) r |= LOCK_SH;
  if (x & 2) r |= LOCK_EX;
  if (x & 4) r |= LOCK_NB;
  if (x & 8) r |= LOCK_UN;
  return r;
}

int XlatWait(int x) {
  int r = 0;
  if (x & 1) r |= WNOHANG;
  if (x & 2) r |= WUNTRACED;
  if (x & 8) r |= WCONTINUED;
  return r;
}

int XlatMapFlags(int x) {
  int r = 0;
  if (x & 1) r |= MAP_SHARED;
  if (x & 2) r |= MAP_PRIVATE;
  if (x & 16) r |= MAP_FIXED;
  if (x & 32) r |= MAP_ANONYMOUS;
  return r;
}

int XlatMsyncFlags(int x) {
  unsigned res = 0;
  if (x & 1) res |= MS_ASYNC;
  if (x & 2) res |= MS_INVALIDATE;
  if (x & 4) res |= MS_SYNC;
  return res;
}

int XlatClock(int x) {
  switch (x) {
    XLAT(0, CLOCK_REALTIME);
    XLAT(1, CLOCK_MONOTONIC);
    XLAT(2, CLOCK_PROCESS_CPUTIME_ID);
#ifdef CLOCK_MONOTONIC_RAW
    XLAT(4, CLOCK_MONOTONIC_RAW);
#endif
    default:
      return x;
  }
}

int XlatAtf(int x) {
  int res = 0;
  if (x & 0x0100) res |= AT_SYMLINK_NOFOLLOW;
  if (x & 0x0200) res |= AT_REMOVEDIR;
  if (x & 0x0400) res |= AT_SYMLINK_FOLLOW;
  if (x & 0x1000) res |= AT_EMPTY_PATH;
  return res;
}

int XlatOpenMode(int flags) {
  switch (flags & 3) {
    case 0:
      return O_RDONLY;
    case 1:
      return O_WRONLY;
    case 2:
      return O_RDWR;
    default:
      for (;;) (void)0;
  }
}

int XlatOpenFlags(int flags) {
  int res;
  res = XlatOpenMode(flags);
  if (flags & 0x00400) res |= O_APPEND;
  if (flags & 0x00040) res |= O_CREAT;
  if (flags & 0x00080) res |= O_EXCL;
  if (flags & 0x00200) res |= O_TRUNC;
  if (flags & 0x00800) res |= O_NDELAY;
  if (flags & 0x04000) res |= O_DIRECT;
  if (flags & 0x10000) res |= O_DIRECTORY;
  if (flags & 0x20000) res |= O_NOFOLLOW;
  if (flags & 0x80000) res |= O_CLOEXEC;
  if (flags & 0x00100) res |= O_NOCTTY;
#ifdef O_ASYNC
  if (flags & 0x02000) res |= O_ASYNC;
#endif
#ifdef O_NOATIME
  if (flags & 0x40000) res |= O_NOATIME;
#endif
#ifdef O_DSYNC
  if (flags & 0x000001000) res |= O_DSYNC;
#endif
#ifdef O_SYNC
  if ((flags & 0x00101000) == 0x00101000) res |= O_SYNC;
#endif
  return res;
}

int XlatFcntlCmd(int x) {
  switch (x) {
    XLAT(1, F_GETFD);
    XLAT(2, F_SETFD);
    XLAT(3, F_GETFL);
    XLAT(4, F_SETFL);
    default:
      return einval();
  }
}

int XlatFcntlArg(int x) {
  switch (x) {
    XLAT(0, 0);
    XLAT(1, FD_CLOEXEC);
    XLAT(0x0800, O_NONBLOCK);
    default:
      return einval();
  }
}

int XlatAdvice(int x) {
  switch (x) {
    XLAT(0, MADV_NORMAL);
    XLAT(1, MADV_RANDOM);
    XLAT(2, MADV_SEQUENTIAL);
    XLAT(3, MADV_WILLNEED);
    XLAT(4, MADV_DONTNEED);
    XLAT(8, MADV_FREE);
    XLAT(12, MADV_MERGEABLE);
    default:
      return einval();
  }
}

void XlatSockaddrToHost(struct sockaddr_in *dst,
                        const struct sockaddr_in_bits *src) {
  memset(dst, 0, sizeof(*dst));
  dst->sin_family = XlatSocketFamily(Read16(src->sin_family));
  dst->sin_port = src->sin_port;
  dst->sin_addr.s_addr = src->sin_addr;
}

void XlatSockaddrToLinux(struct sockaddr_in_bits *dst,
                         const struct sockaddr_in *src) {
  memset(dst, 0, sizeof(*dst));
  Write16(dst->sin_family, UnXlatSocketFamily(src->sin_family));
  dst->sin_port = src->sin_port;
  dst->sin_addr = src->sin_addr.s_addr;
}

void XlatStatToLinux(struct stat_bits *dst, const struct stat *src) {
  Write64(dst->st_dev, src->st_dev);
  Write64(dst->st_ino, src->st_ino);
  Write64(dst->st_nlink, src->st_nlink);
  Write32(dst->st_mode, src->st_mode);
  Write32(dst->st_uid, src->st_uid);
  Write32(dst->st_gid, src->st_gid);
  Write32(dst->__pad, 0);
  Write64(dst->st_rdev, src->st_rdev);
  Write64(dst->st_size, src->st_size);
  Write64(dst->st_blksize, src->st_blksize);
  Write64(dst->st_blocks, src->st_blocks);
  Write64(dst->st_dev, src->st_dev);
  Write64(dst->st_atim.tv_sec, src->st_atim.tv_sec);
  Write64(dst->st_atim.tv_nsec, src->st_atim.tv_nsec);
  Write64(dst->st_mtim.tv_sec, src->st_mtim.tv_sec);
  Write64(dst->st_mtim.tv_nsec, src->st_mtim.tv_nsec);
  Write64(dst->st_ctim.tv_sec, src->st_ctim.tv_sec);
  Write64(dst->st_ctim.tv_nsec, src->st_ctim.tv_nsec);
}

void XlatRusageToLinux(struct rusage_bits *dst, const struct rusage *src) {
  Write64(dst->ru_utime.tv_sec, src->ru_utime.tv_sec);
  Write64(dst->ru_utime.tv_usec, src->ru_utime.tv_usec);
  Write64(dst->ru_stime.tv_sec, src->ru_stime.tv_sec);
  Write64(dst->ru_stime.tv_usec, src->ru_stime.tv_usec);
  Write64(dst->ru_maxrss, src->ru_maxrss);
  Write64(dst->ru_ixrss, src->ru_ixrss);
  Write64(dst->ru_idrss, src->ru_idrss);
  Write64(dst->ru_isrss, src->ru_isrss);
  Write64(dst->ru_minflt, src->ru_minflt);
  Write64(dst->ru_majflt, src->ru_majflt);
  Write64(dst->ru_nswap, src->ru_nswap);
  Write64(dst->ru_inblock, src->ru_inblock);
  Write64(dst->ru_oublock, src->ru_oublock);
  Write64(dst->ru_msgsnd, src->ru_msgsnd);
  Write64(dst->ru_msgrcv, src->ru_msgrcv);
  Write64(dst->ru_nsignals, src->ru_nsignals);
  Write64(dst->ru_nvcsw, src->ru_nvcsw);
  Write64(dst->ru_nivcsw, src->ru_nivcsw);
}

void XlatItimervalToLinux(struct itimerval_bits *dst,
                          const struct itimerval *src) {
  Write64(dst->it_interval.tv_sec, src->it_interval.tv_sec);
  Write64(dst->it_interval.tv_usec, src->it_interval.tv_usec);
  Write64(dst->it_value.tv_sec, src->it_value.tv_sec);
  Write64(dst->it_value.tv_usec, src->it_value.tv_usec);
}

void XlatLinuxToItimerval(struct itimerval *dst,
                          const struct itimerval_bits *src) {
  dst->it_interval.tv_sec = Read64(src->it_interval.tv_sec);
  dst->it_interval.tv_usec = Read64(src->it_interval.tv_usec);
  dst->it_value.tv_sec = Read64(src->it_value.tv_sec);
  dst->it_value.tv_usec = Read64(src->it_value.tv_usec);
}

void XlatWinsizeToLinux(struct winsize_bits *dst, const struct winsize *src) {
  memset(dst, 0, sizeof(*dst));
  Write16(dst->ws_row, src->ws_row);
  Write16(dst->ws_col, src->ws_col);
}

void XlatSigsetToLinux(uint8_t dst[8], const sigset_t *src) {
  int i;
  uint64_t x;
  for (x = i = 0; i < 64; ++i) {
    if (sigismember(src, i + 1)) {
      x |= 1ull << i;
    }
  }
  Write64(dst, x);
}

void XlatLinuxToSigset(sigset_t *dst, const uint8_t src[8]) {
  int i;
  uint64_t x;
  x = Read64(src);
  sigemptyset(dst);
  for (i = 0; i < 64; ++i) {
    if ((1ull << i) & x) {
      sigaddset(dst, i + 1);
    }
  }
}

static int XlatTermiosCflag(int x) {
  int r = 0;
  if (x & 0x0001) r |= ISIG;
  if (x & 0x0040) r |= CSTOPB;
  if (x & 0x0080) r |= CREAD;
  if (x & 0x0100) r |= PARENB;
  if (x & 0x0200) r |= PARODD;
  if (x & 0x0400) r |= HUPCL;
  if (x & 0x0800) r |= CLOCAL;
  if ((x & 0x0030) == 0x0010) {
    r |= CS6;
  } else if ((x & 0x0030) == 0x0020) {
    r |= CS7;
  } else if ((x & 0x0030) == 0x0030) {
    r |= CS8;
  }
  return r;
}

static int UnXlatTermiosCflag(int x) {
  int r = 0;
  if (x & ISIG) r |= 0x0001;
  if (x & CSTOPB) r |= 0x0040;
  if (x & CREAD) r |= 0x0080;
  if (x & PARENB) r |= 0x0100;
  if (x & PARODD) r |= 0x0200;
  if (x & HUPCL) r |= 0x0400;
  if (x & CLOCAL) r |= 0x0800;
  if ((x & CSIZE) == CS5) {
    r |= 0x0000;
  } else if ((x & CSIZE) == CS6) {
    r |= 0x0010;
  } else if ((x & CSIZE) == CS7) {
    r |= 0x0020;
  } else if ((x & CSIZE) == CS8) {
    r |= 0x0030;
  }
  return r;
}

static int XlatTermiosLflag(int x) {
  int r = 0;
  if (x & 0x0001) r |= ISIG;
  if (x & 0x0002) r |= ICANON;
  if (x & 0x0008) r |= ECHO;
  if (x & 0x0010) r |= ECHOE;
  if (x & 0x0020) r |= ECHOK;
  if (x & 0x0040) r |= ECHONL;
  if (x & 0x0080) r |= NOFLSH;
  if (x & 0x0100) r |= TOSTOP;
  if (x & 0x8000) r |= IEXTEN;
#ifdef ECHOCTL
  if (x & 0x0200) r |= ECHOCTL;
#endif
#ifdef ECHOPRT
  if (x & 0x0400) r |= ECHOPRT;
#endif
#ifdef ECHOKE
  if (x & 0x0800) r |= ECHOKE;
#endif
#ifdef FLUSHO
  if (x & 0x1000) r |= FLUSHO;
#endif
#ifdef PENDIN
  if (x & 0x4000) r |= PENDIN;
#endif
#ifdef XCASE
  if (x & 0x0004) r |= XCASE;
#endif
  return r;
}

static int UnXlatTermiosLflag(int x) {
  int r = 0;
  if (x & ISIG) r |= 0x0001;
  if (x & ICANON) r |= 0x0002;
  if (x & ECHO) r |= 0x0008;
  if (x & ECHOE) r |= 0x0010;
  if (x & ECHOK) r |= 0x0020;
  if (x & ECHONL) r |= 0x0040;
  if (x & NOFLSH) r |= 0x0080;
  if (x & TOSTOP) r |= 0x0100;
  if (x & IEXTEN) r |= 0x8000;
#ifdef ECHOCTL
  if (x & ECHOCTL) r |= 0x0200;
#endif
#ifdef ECHOPRT
  if (x & ECHOPRT) r |= 0x0400;
#endif
#ifdef ECHOKE
  if (x & ECHOKE) r |= 0x0800;
#endif
#ifdef FLUSHO
  if (x & FLUSHO) r |= 0x1000;
#endif
#ifdef PENDIN
  if (x & PENDIN) r |= 0x4000;
#endif
#ifdef XCASE
  if (x & XCASE) r |= 0x0004;
#endif
  return r;
}

static int XlatTermiosIflag(int x) {
  int r = 0;
  if (x & 0x0001) r |= IGNBRK;
  if (x & 0x0002) r |= BRKINT;
  if (x & 0x0004) r |= IGNPAR;
  if (x & 0x0008) r |= PARMRK;
  if (x & 0x0010) r |= INPCK;
  if (x & 0x0020) r |= ISTRIP;
  if (x & 0x0040) r |= INLCR;
  if (x & 0x0080) r |= IGNCR;
  if (x & 0x0100) r |= ICRNL;
  if (x & 0x0400) r |= IXON;
  if (x & 0x0800) r |= IXANY;
  if (x & 0x1000) r |= IXOFF;
#ifdef IMAXBEL
  if (x & 0x2000) r |= IMAXBEL;
#endif
#ifdef IUTF8
  if (x & 0x4000) r |= IUTF8;
#endif
#ifdef IUCLC
  if (x & 0x0200) r |= IUCLC;
#endif
  return r;
}

static int UnXlatTermiosIflag(int x) {
  int r = 0;
  if (x & IGNBRK) r |= 0x0001;
  if (x & BRKINT) r |= 0x0002;
  if (x & IGNPAR) r |= 0x0004;
  if (x & PARMRK) r |= 0x0008;
  if (x & INPCK) r |= 0x0010;
  if (x & ISTRIP) r |= 0x0020;
  if (x & INLCR) r |= 0x0040;
  if (x & IGNCR) r |= 0x0080;
  if (x & ICRNL) r |= 0x0100;
  if (x & IXON) r |= 0x0400;
  if (x & IXANY) r |= 0x0800;
  if (x & IXOFF) r |= 0x1000;
#ifdef IMAXBEL
  if (x & IMAXBEL) r |= 0x2000;
#endif
#ifdef IUTF8
  if (x & IUTF8) r |= 0x4000;
#endif
#ifdef IUCLC
  if (x & IUCLC) r |= 0x0200;
#endif
  return r;
}

static int XlatTermiosOflag(int x) {
  int r = 0;
  if (x & 0x0001) r |= OPOST;
#ifdef ONLCR
  if (x & 0x0004) r |= ONLCR;
#endif
#ifdef OCRNL
  if (x & 0x0008) r |= OCRNL;
#endif
#ifdef ONOCR
  if (x & 0x0010) r |= ONOCR;
#endif
#ifdef ONLRET
  if (x & 0x0020) r |= ONLRET;
#endif
#ifdef OFILL
  if (x & 0x0040) r |= OFILL;
#endif
#ifdef OFDEL
  if (x & 0x0080) r |= OFDEL;
#endif
#ifdef NLDLY
  if ((x & 0x0100) == 0x0000) {
    r |= NL0;
  } else if ((x & 0x0100) == 0x0100) {
    r |= NL1;
#ifdef NL2
  } else if ((x & 0x0100) == 0x0000) {
    r |= NL2;
#endif
#ifdef NL3
  } else if ((x & 0x0100) == 0x0000) {
    r |= NL3;
#endif
  }
#endif
#ifdef CRDLY
  if ((x & 0x0600) == 0x0000) {
    r |= CR0;
  } else if ((x & 0x0600) == 0x0200) {
    r |= CR1;
  } else if ((x & 0x0600) == 0x0400) {
    r |= CR2;
  } else if ((x & 0x0600) == 0x0600) {
    r |= CR3;
  }
#endif
#ifdef TABDLY
  if ((x & 0x1800) == 0x0000) {
    r |= TAB0;
#ifdef TAB1
  } else if ((x & 0x1800) == 0x0800) {
    r |= TAB1;
#endif
#ifdef TAB1
  } else if ((x & 0x1800) == 0x1000) {
    r |= TAB2;
#endif
  } else if ((x & 0x1800) == 0x1800) {
    r |= TAB3;
  }
#endif
#ifdef BSDLY
  if ((x & 0x2000) == 0x0000) {
    r |= BS0;
  } else if ((x & 0x2000) == 0x2000) {
    r |= BS1;
  }
#endif
#ifdef VTBDLY
  if ((x & 0x4000) == 0x0000) {
    r |= VT0;
  } else if ((x & 0x4000) == 0x4000) {
    r |= VT1;
  }
#endif
#ifdef FFBDLY
  if ((x & 0x8000) == 0x0000) {
    r |= FF0;
  } else if ((x & 0x8000) == 0x8000) {
    r |= FF1;
  }
#endif
#ifdef OLCUC
  if (x & 0x0002) r |= OLCUC;
#endif
  return r;
}

static int UnXlatTermiosOflag(int x) {
  int r = 0;
  if (x & OPOST) r |= 0x0001;
#ifdef ONLCR
  if (x & ONLCR) r |= 0x0004;
#endif
#ifdef OCRNL
  if (x & OCRNL) r |= 0x0008;
#endif
#ifdef ONOCR
  if (x & ONOCR) r |= 0x0010;
#endif
#ifdef ONLRET
  if (x & ONLRET) r |= 0x0020;
#endif
#ifdef OFILL
  if (x & OFILL) r |= 0x0040;
#endif
#ifdef OFDEL
  if (x & OFDEL) r |= 0x0080;
#endif
#ifdef NLDLY
  if ((x & NLDLY) == NL0) {
    r |= 0x0000;
  } else if ((x & NLDLY) == NL1) {
    r |= 0x0100;
#ifdef NL2
  } else if ((x & NLDLY) == NL2) {
    r |= 0x0000;
#endif
#ifdef NL3
  } else if ((x & NLDLY) == NL3) {
    r |= 0x0000;
#endif
  }
#endif
#ifdef CRDLY
  if ((x & CRDLY) == CR0) {
    r |= 0x0000;
  } else if ((x & CRDLY) == CR1) {
    r |= 0x0200;
  } else if ((x & CRDLY) == CR2) {
    r |= 0x0400;
  } else if ((x & CRDLY) == CR3) {
    r |= 0x0600;
  }
#endif
#ifdef TABDLY
  if ((x & TABDLY) == TAB0) {
    r |= 0x0000;
#ifdef TAB1
  } else if ((x & TABDLY) == TAB1) {
    r |= 0x0800;
#endif
#ifdef TAB2
  } else if ((x & TABDLY) == TAB2) {
    r |= 0x1000;
#endif
  } else if ((x & TABDLY) == TAB3) {
    r |= 0x1800;
  }
#endif
#ifdef BSDLY
  if ((x & BSDLY) == BS0) {
    r |= 0x0000;
  } else if ((x & BSDLY) == BS1) {
    r |= 0x2000;
  }
#endif
#ifdef VTDLY
  if ((x & VTDLY) == VT0) {
    r |= 0x0000;
  } else if ((x & VTDLY) == VT1) {
    r |= 0x4000;
  }
#endif
#ifdef FFDLY
  if ((x & FFDLY) == FF0) {
    r |= 0x0000;
  } else if ((x & FFDLY) == FF1) {
    r |= 0x8000;
  }
#endif
#ifdef OLCUC
  if (x & OLCUC) r |= 0x0002;
#endif
  return r;
}

static void XlatTermiosCc(struct termios *dst, const struct termios_bits *src) {
  dst->c_cc[VINTR] = src->c_cc[0];
  dst->c_cc[VQUIT] = src->c_cc[1];
  dst->c_cc[VERASE] = src->c_cc[2];
  dst->c_cc[VKILL] = src->c_cc[3];
  dst->c_cc[VEOF] = src->c_cc[4];
  dst->c_cc[VTIME] = src->c_cc[5];
  dst->c_cc[VMIN] = src->c_cc[6];
  dst->c_cc[VSTART] = src->c_cc[8];
  dst->c_cc[VSTOP] = src->c_cc[9];
  dst->c_cc[VSUSP] = src->c_cc[10];
  dst->c_cc[VEOL] = src->c_cc[11];
#ifdef VSWTC
  dst->c_cc[VSWTC] = src->c_cc[7];
#endif
#ifdef VREPRINT
  dst->c_cc[VREPRINT] = src->c_cc[12];
#endif
#ifdef VDISCARD
  dst->c_cc[VDISCARD] = src->c_cc[13];
#endif
#ifdef VWERASE
  dst->c_cc[VWERASE] = src->c_cc[14];
#endif
#ifdef VLNEXT
  dst->c_cc[VLNEXT] = src->c_cc[15];
#endif
#ifdef VEOL2
  dst->c_cc[VEOL2] = src->c_cc[16];
#endif
}

static void UnXlatTermiosCc(struct termios_bits *dst,
                            const struct termios *src) {
  dst->c_cc[0] = src->c_cc[VINTR];
  dst->c_cc[1] = src->c_cc[VQUIT];
  dst->c_cc[2] = src->c_cc[VERASE];
  dst->c_cc[3] = src->c_cc[VKILL];
  dst->c_cc[4] = src->c_cc[VEOF];
  dst->c_cc[5] = src->c_cc[VTIME];
  dst->c_cc[6] = src->c_cc[VMIN];
  dst->c_cc[8] = src->c_cc[VSTART];
  dst->c_cc[9] = src->c_cc[VSTOP];
  dst->c_cc[10] = src->c_cc[VSUSP];
  dst->c_cc[11] = src->c_cc[VEOL];
#ifdef VSWTC
  dst->c_cc[7] = src->c_cc[VSWTC];
#endif
#ifdef VREPRINT
  dst->c_cc[12] = src->c_cc[VREPRINT];
#endif
#ifdef VDISCARD
  dst->c_cc[13] = src->c_cc[VDISCARD];
#endif
#ifdef VWERASE
  dst->c_cc[14] = src->c_cc[VWERASE];
#endif
#ifdef VLNEXT
  dst->c_cc[15] = src->c_cc[VLNEXT];
#endif
#ifdef VEOL2
  dst->c_cc[16] = src->c_cc[VEOL2];
#endif
}

void XlatLinuxToTermios(struct termios *dst, const struct termios_bits *src) {
  memset(dst, 0, sizeof(*dst));
  dst->c_iflag = XlatTermiosIflag(Read32(src->c_iflag));
  dst->c_oflag = XlatTermiosOflag(Read32(src->c_oflag));
  dst->c_cflag = XlatTermiosCflag(Read32(src->c_cflag));
  dst->c_lflag = XlatTermiosLflag(Read32(src->c_lflag));
  XlatTermiosCc(dst, src);
}

void XlatTermiosToLinux(struct termios_bits *dst, const struct termios *src) {
  memset(dst, 0, sizeof(*dst));
  Write32(dst->c_iflag, UnXlatTermiosIflag(src->c_iflag));
  Write32(dst->c_oflag, UnXlatTermiosOflag(src->c_oflag));
  Write32(dst->c_cflag, UnXlatTermiosCflag(src->c_cflag));
  Write32(dst->c_lflag, UnXlatTermiosLflag(src->c_lflag));
  UnXlatTermiosCc(dst, src);
}
