/*-*- 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â
ââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââ¡
â                                                                              â
â Cosmopolitan Linenoise ââ guerrilla line editing library against the         â
â notion that a library for user-friendly pseudoteletypewriter command         â
â sessions using ANSI Standard X3.64 control sequences must have 100k+         â
â lines of GPL workarounds to 300 baud and bygone commercial rivalries         â
â                                                                              â
â CHANGES                                                                      â
â                                                                              â
â   - Remove bell                                                              â
â   - Add kill ring                                                            â
â   - Fix flickering                                                           â
â   - Add UTF-8 editing                                                        â
â   - Add CTRL-R search                                                        â
â   - Support unlimited lines                                                  â
â   - React to terminal resizing                                               â
â   - Support terminal flow control                                            â
â   - Make history loading 10x faster                                          â
â   - Make multiline mode the only mode                                        â
â   - Support unlimited input line length                                      â
â   - Restore raw mode on process foregrounding                                â
â   - Make source code compatible with C++ compilers                           â
â   - Fix corruption issues by using generalized parsing                       â
â   - Implement nearly all GNU readline editing shortcuts                      â
â   - Remove heavyweight dependencies like printf/sprintf                      â
â   - Remove ISIGâ^CâEAGAIN hack and use ephemeral handlers                    â
â   - Support running on Windows in MinTTY or CMD.EXE on Win10+                â
â   - Support diacratics, ÑÑÑÑÐºÐ¸Ð¹, ÎÎ»Î»Î·Î½Î¹ÎºÎ¬, ä¸­å½äºº, æ¥æ¬èª, íêµ­ì¸            â
â                                                                              â
â SHORTCUTS                                                                    â
â                                                                              â
â   CTRL-E         END                                                         â
â   CTRL-A         START                                                       â
â   CTRL-B         BACK                                                        â
â   CTRL-F         FORWARD                                                     â
â   CTRL-L         CLEAR                                                       â
â   CTRL-H         BACKSPACE                                                   â
â   CTRL-D         DELETE                                                      â
â   CTRL-D         EOF (IF EMPTY)                                              â
â   CTRL-N         NEXT HISTORY                                                â
â   CTRL-P         PREVIOUS HISTORY                                            â
â   CTRL-R         SEARCH HISTORY                                              â
â   CTRL-G         CANCEL SEARCH                                               â
â   ALT-<          BEGINNING OF HISTORY                                        â
â   ALT->          END OF HISTORY                                              â
â   ALT-F          FORWARD WORD                                                â
â   ALT-B          BACKWARD WORD                                               â
â   CTRL-RIGHT     FORWARD WORD                                                â
â   CTRL-LEFT      BACKWARD WORD                                               â
â   CTRL-ALT-F     FORWARD EXPR                                                â
â   CTRL-ALT-B     BACKWARD EXPR                                               â
â   ALT-RIGHT      FORWARD EXPR                                                â
â   ALT-LEFT       BACKWARD EXPR                                               â
â   ALT-SHIFT-B    BARF EXPR                                                   â
â   ALT-SHIFT-S    SLURP EXPR                                                  â
â   CTRL-K         KILL LINE FORWARDS                                          â
â   CTRL-U         KILL LINE BACKWARDS                                         â
â   ALT-H          KILL WORD BACKWARDS                                         â
â   CTRL-W         KILL WORD BACKWARDS                                         â
â   CTRL-ALT-H     KILL WORD BACKWARDS                                         â
â   ALT-D          KILL WORD FORWARDS                                          â
â   CTRL-Y         YANK                                                        â
â   ALT-Y          ROTATE KILL RING AND YANK AGAIN                             â
â   ALT-\          SQUEEZE ADJACENT WHITESPACE                                 â
â   CTRL-T         TRANSPOSE                                                   â
â   ALT-T          TRANSPOSE WORD                                              â
â   ALT-U          UPPERCASE WORD                                              â
â   ALT-L          LOWERCASE WORD                                              â
â   ALT-C          CAPITALIZE WORD                                             â
â   CTRL-C CTRL-C  INTERRUPT PROCESS                                           â
â   CTRL-Z         SUSPEND PROCESS                                             â
â   CTRL-\         QUIT PROCESS                                                â
â   CTRL-S         PAUSE OUTPUT                                                â
â   CTRL-Q         UNPAUSE OUTPUT (IF PAUSED)                                  â
â   CTRL-Q         ESCAPED INSERT                                              â
â   CTRL-SPACE     SET MARK                                                    â
â   CTRL-X CTRL-X  GOTO MARK                                                   â
â   PROTIP         REMAP CAPS LOCK TO CTRL                                     â
â                                                                              â
â EXAMPLE                                                                      â
â                                                                              â
â   // should be ~80kb statically linked                                       â
â   // will save history to ~/.foo_history                                     â
â   // cc -fno-jump-tables -Os -o foo foo.c linenoise.c                        â
â   main() {                                                                   â
â       char *line;                                                            â
â       while ((line = linenoiseWithHistory("IN> ", "foo"))) {                 â
â           fputs("OUT> ", stdout);                                            â
â           fputs(line, stdout);                                               â
â           fputs("\n", stdout);                                               â
â           free(line);                                                        â
â       }                                                                      â
â   }                                                                          â
â                                                                              â
ââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââ¡
â                                                                              â
â Copyright 2018-2021 Justine Tunney <jtunney@gmail.com>                       â
â Copyright 2010-2016 Salvatore Sanfilippo <antirez@gmail.com>                 â
â Copyright 2010-2013 Pieter Noordhuis <pcnoordhuis@gmail.com>                 â
â                                                                              â
â All rights reserved.                                                         â
â                                                                              â
â Redistribution and use in source and binary forms, with or without           â
â modification, are permitted provided that the following conditions are       â
â met:                                                                         â
â                                                                              â
â  *  Redistributions of source code must retain the above copyright           â
â     notice, this list of conditions and the following disclaimer.            â
â                                                                              â
â  *  Redistributions in binary form must reproduce the above copyright        â
â     notice, this list of conditions and the following disclaimer in the      â
â     documentation and/or other materials provided with the distribution.     â
â                                                                              â
â THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS          â
â "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT            â
â LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR        â
â A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT         â
â HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,       â
â SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT             â
â LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,        â
â DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY        â
â THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT          â
â (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE        â
â OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.         â
â                                                                              â
ââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââ*/
#include "third_party/linenoise/linenoise.h"
#include "libc/assert.h"
#include "libc/calls/calls.h"
#include "libc/calls/sig.internal.h"
#include "libc/calls/struct/sigaction.h"
#include "libc/calls/struct/stat.h"
#include "libc/calls/termios.h"
#include "libc/calls/ttydefaults.h"
#include "libc/calls/weirdtypes.h"
#include "libc/dce.h"
#include "libc/errno.h"
#include "libc/fmt/conv.h"
#include "libc/intrin/asan.internal.h"
#include "libc/intrin/bits.h"
#include "libc/intrin/bsr.h"
#include "libc/intrin/nomultics.internal.h"
#include "libc/intrin/strace.internal.h"
#include "libc/intrin/tpenc.h"
#include "libc/log/check.h"
#include "libc/log/log.h"
#include "libc/macros.internal.h"
#include "libc/mem/alg.h"
#include "libc/mem/mem.h"
#include "libc/nexgen32e/rdtsc.h"
#include "libc/nt/version.h"
#include "libc/runtime/runtime.h"
#include "libc/sock/sock.h"
#include "libc/sock/struct/pollfd.h"
#include "libc/stdio/append.h"
#include "libc/stdio/stdio.h"
#include "libc/str/str.h"
#include "libc/str/tab.internal.h"
#include "libc/str/unicode.h"
#include "libc/sysv/consts/fileno.h"
#include "libc/sysv/consts/map.h"
#include "libc/sysv/consts/o.h"
#include "libc/sysv/consts/poll.h"
#include "libc/sysv/consts/prot.h"
#include "libc/sysv/consts/s.h"
#include "libc/sysv/consts/sa.h"
#include "libc/sysv/consts/sig.h"
#include "libc/sysv/consts/termios.h"
#include "libc/sysv/errfuns.h"
#include "net/http/escape.h"
#include "tool/build/lib/case.h"

asm(".ident\t\"\\n\\n\
Cosmopolitan Linenoise (BSD-2)\\n\
Copyright 2018-2020 Justine Tunney <jtunney@gmail.com>\\n\
Copyright 2010-2016 Salvatore Sanfilippo <antirez@gmail.com>\\n\
Copyright 2010-2013 Pieter Noordhuis <pcnoordhuis@gmail.com>\"");

#define LINENOISE_POLL_MS __SIG_POLLING_INTERVAL_MS

#define LINENOISE_MAX_RING    8
#define LINENOISE_MAX_DEBUG   16
#define LINENOISE_MAX_HISTORY 1024

#define LINENOISE_HISTORY_FIRST +LINENOISE_MAX_HISTORY
#define LINENOISE_HISTORY_PREV  +1
#define LINENOISE_HISTORY_NEXT  -1
#define LINENOISE_HISTORY_LAST  -LINENOISE_MAX_HISTORY

#if 0
#define DEBUG(L, ...) linenoiseDebug(L, __VA_ARGS__)
#else
#define DEBUG(L, ...) (void)0
#endif

#define DUFF_ROUTINE_LOOP   0
#define DUFF_ROUTINE_SEARCH 1
#define DUFF_ROUTINE_START  5

#define DUFF_ROUTINE_LABEL(STATE) \
  case STATE:                     \
    linenoiseRefreshLineForce(l); \
    l->state = STATE

#define DUFF_ROUTINE_READ(STATE)                          \
  DUFF_ROUTINE_LABEL(STATE);                              \
  rc = linenoiseRead(l->ifd, seq, sizeof(seq), l, block); \
  if (rc == -1 && errno == EAGAIN) {                      \
    l->state = STATE;                                     \
    return -1;                                            \
  }

#define BLOCKING_READ() rc = linenoiseRead(l->ifd, seq, sizeof(seq), l, false)

struct abuf {
  char *b;
  int len;
  int cap;
};

struct rune {
  unsigned c;
  unsigned n;
};

struct linenoiseRing {
  unsigned i;
  char *p[LINENOISE_MAX_RING];
};

struct linenoiseState {
  int state;          /* state machine */
  int ifd;            /* terminal stdin file descriptor */
  int ofd;            /* terminal stdout file descriptor */
  struct winsize ws;  /* rows and columns in terminal */
  char *buf;          /* edited line buffer */
  const char *prompt; /* prompt to display */
  int hindex;         /* history index */
  int rows;           /* rows being used */
  int oldpos;         /* previous refresh cursor position */
  unsigned debugrow;  /* row for debug display */
  unsigned buflen;    /* edited line buffer size */
  unsigned pos;       /* current buffer index */
  unsigned len;       /* current edited line length */
  unsigned mark;      /* saved cursor position */
  unsigned yi, yj;    /* boundaries of last yank */
  char seq[2][16];    /* keystroke history for yanking code */
  char final;         /* set to true on last update */
  char dirty;         /* if an update was squashed */
  linenoiseCompletions lc;
  struct abuf ab;
  int i, j, perline, itemlen;
  // for reverse search
  int fail, matlen, oldindex, olderpos;
  const char *oldprompt;
};

static const unsigned short kMirrorLeft[][2] = {
    {L'(', L')'},   {L'[', L']'},   {L'{', L'}'},   {L'â', L'â'},
    {L'â½', L'â¾'},   {L'â', L'â'},   {L'â', L'â'},   {L'â', L'â'},
    {L'â©', L'âª'}, {L'â¨', L'â©'},   {L'âª', L'â«'},   {L'â¬', L'â­'},
    {L'â®', L'â¯'},   {L'â°', L'â±'},   {L'â²', L'â³'},   {L'â´', L'âµ'},
    {L'â', L'â'},   {L'â¦', L'â§'},   {L'â¨', L'â©'},   {L'âª', L'â«'},
    {L'â¬', L'â­'},   {L'â®', L'â¯'},   {L'â¦', L'â¦'},   {L'â¦', L'â¦'},
    {L'â¦', L'â¦'},   {L'â¦', L'â¦'},   {L'â¦', L'â¦'},   {L'â¦', L'â¦'},
    {L'â¦', L'â¦'},   {L'â¦', L'â¦'},   {L'â¦', L'â¦'},   {L'â¦', L'â¦'},
    {L'â§', L'â§'},   {L'â§', L'â§'},   {L'â§¼', L'â§½'},   {L'ï¹', L'ï¹'},
    {L'ï¹', L'ï¹'}, {L'ï¹', L'ï¹'}, {L'ï¼', L'ï¼'}, {L'ï¼»', L'ï¼½'},
    {L'ï½', L'ï½'}, {L'ï½¢', L'ï½£'},
};

static const unsigned short kMirrorRight[][2] = {
    {L')', L'('},   {L']', L'['},   {L'}', L'{'},   {L'â', L'â'},
    {L'â¾', L'â½'},   {L'â', L'â'},   {L'â', L'â'},   {L'â', L'â'},
    {L'âª', L'â©'}, {L'â©', L'â¨'},   {L'â«', L'âª'},   {L'â­', L'â¬'},
    {L'â¯', L'â®'},   {L'â±', L'â°'},   {L'â³', L'â²'},   {L'âµ', L'â´'},
    {L'â', L'â'},   {L'â§', L'â¦'},   {L'â©', L'â¨'},   {L'â«', L'âª'},
    {L'â­', L'â¬'},   {L'â¯', L'â®'},   {L'â¦', L'â¦'},   {L'â¦', L'â¦'},
    {L'â¦', L'â¦'},   {L'â¦', L'â¦'},   {L'â¦', L'â¦'},   {L'â¦', L'â¦'},
    {L'â¦', L'â¦'},   {L'â¦', L'â¦'},   {L'â¦', L'â¦'},   {L'â¦', L'â¦'},
    {L'â§', L'â§'},   {L'â§', L'â§'},   {L'â§½', L'â§¼'},   {L'ï¹', L'ï¹'},
    {L'ï¹', L'ï¹'}, {L'ï¹', L'ï¹'}, {L'ï¼', L'ï¼'}, {L'ï¼½', L'ï¼»'},
    {L'ï½', L'ï½'}, {L'ï½£', L'ï½¢'},
};

static const char *const kUnsupported[] = {"dumb", "cons25", "emacs"};

static int gotint;
static int gotcont;
static int gotwinch;
static char maskmode;
static char ispaused;
static char iscapital;
static int historylen;
static signed char rawmode = -1;
static struct linenoiseRing ring;
static struct sigaction orig_int;
static struct sigaction orig_quit;
static struct sigaction orig_cont;
static struct sigaction orig_winch;
static struct termios orig_termios;
static char *history[LINENOISE_MAX_HISTORY];
static linenoiseXlatCallback *xlatCallback;
static linenoiseHintsCallback *hintsCallback;
static linenoiseFreeHintsCallback *freeHintsCallback;
static linenoiseCompletionCallback *completionCallback;

static unsigned GetMirror(const unsigned short A[][2], size_t n, unsigned c) {
  int l, m, r;
  l = 0;
  r = n - 1;
  while (l <= r) {
    m = (l + r) >> 1;
    if (A[m][0] < c) {
      l = m + 1;
    } else if (A[m][0] > c) {
      r = m - 1;
    } else {
      return A[m][1];
    }
  }
  return 0;
}

static unsigned GetMirrorLeft(unsigned c) {
  return GetMirror(kMirrorRight, ARRAYLEN(kMirrorRight), c);
}

static unsigned GetMirrorRight(unsigned c) {
  return GetMirror(kMirrorLeft, ARRAYLEN(kMirrorLeft), c);
}

static int isxseparator(wint_t c) {
  return iswseparator(c) && !GetMirrorLeft(c) && !GetMirrorRight(c);
}

static int notwseparator(wint_t c) {
  return !iswseparator(c);
}

static int iswname(wint_t c) {
  return !iswseparator(c) || c == '_' || c == '-' || c == '.' || c == ':';
}

static int notwname(wint_t c) {
  return !iswname(c);
}

static void linenoiseOnInt(int sig) {
  gotint = sig;
}

static void linenoiseOnCont(int sig) {
  gotcont = sig;
}

static void linenoiseOnWinch(int sig) {
  gotwinch = sig;
}

static wint_t Capitalize(wint_t c) {
  if (!iscapital) {
    c = towupper(c);
    iscapital = 1;
  }
  return c;
}

static struct rune DecodeUtf8(int c) {
  struct rune r;
  if (c < 252) {
    r.n = _bsr(255 & ~c);
    r.c = c & (((1 << r.n) - 1) | 3);
    r.n = 6 - r.n;
  } else {
    r.c = c & 3;
    r.n = 5;
  }
  return r;
}

static struct rune GetUtf8(const char *p, size_t n) {
  struct rune r;
  if ((r.n = r.c = 0) < n && (r.c = p[r.n++] & 255) >= 0300) {
    r.c = DecodeUtf8(r.c).c;
    while (r.n < n && (p[r.n] & 0300) == 0200) {
      r.c = r.c << 6 | p[r.n++] & 077;
    }
  }
  return r;
}

static size_t GetFdSize(int fd) {
  struct stat st;
  st.st_size = 0;
  fstat(fd, &st);
  return st.st_size;
}

static char IsCharDev(int fd) {
  struct stat st;
  st.st_mode = 0;
  fstat(fd, &st);
  return (st.st_mode & S_IFMT) == S_IFCHR;
}

static int linenoiseIsUnsupportedTerm(void) {
  int i;
  char *term;
  static char once, res;
  if (!once) {
    if (IsWindows() && !IsAtLeastWindows10()) {
      res = 1;
    } else if ((term = getenv("TERM"))) {
      for (i = 0; i < sizeof(kUnsupported) / sizeof(*kUnsupported); i++) {
        if (!strcasecmp(term, kUnsupported[i])) {
          res = 1;
          break;
        }
      }
    }
    once = 1;
  }
  return res;
}

int linenoiseIsTerminal(void) {
  static int once, res;
  if (!once) {
    res = isatty(fileno(stdin)) && isatty(fileno(stdout)) &&
          !linenoiseIsUnsupportedTerm();
    once = 1;
  }
  return res;
}

int linenoiseIsTeletype(void) {
  static int once, res;
  if (!once) {
    res = linenoiseIsTerminal() ||
          (IsCharDev(fileno(stdin)) && IsCharDev(fileno(stdout)));
    once = 1;
  }
  return res;
}

char *linenoiseGetLine(FILE *f) {
  ssize_t rc;
  char *p = 0;
  size_t n, c = 0;
  if ((rc = getdelim(&p, &c, '\n', f)) != EOF) {
    for (n = rc; n; --n) {
      if (p[n - 1] == '\r' || p[n - 1] == '\n') {
        p[n - 1] = 0;
      } else {
        break;
      }
    }
    return p;
  } else {
    free(p);
    return 0;
  }
}

static const char *FindSubstringReverse(const char *p, size_t n, const char *q,
                                        size_t m) {
  size_t i;
  if (m <= n) {
    n -= m;
    do {
      for (i = 0; i < m; ++i) {
        if (kToLower[p[n + i] & 255] != kToLower[q[i] & 255]) {
          break;
        }
      }
      if (kToLower[i & 255] == kToLower[m & 255]) {
        return p + n;
      }
    } while (n--);
  }
  return 0;
}

static int ParseUnsigned(const char *s, void *e) {
  int c, x;
  for (x = 0; (c = *s++);) {
    if ('0' <= c && c <= '9') {
      x = MIN(c - '0' + x * 10, 32767);
    } else {
      break;
    }
  }
  if (e) *(const char **)e = s;
  return x;
}

static char *FormatUnsigned(char *p, unsigned x) {
  char t;
  size_t i, a, b;
  i = 0;
  do {
    p[i++] = x % 10 + '0';
    x = x / 10;
  } while (x > 0);
  p[i] = '\0';
  if (i) {
    for (a = 0, b = i - 1; a < b; ++a, --b) {
      t = p[a];
      p[a] = p[b];
      p[b] = t;
    }
  }
  return p + i;
}

static char HasPendingInput(int fd) {
  return poll((struct pollfd[]){{fd, POLLIN}}, 1, 0) == 1;
}

/**
 * Returns UNICODE CJK Monospace Width of string.
 *
 * Control codes and ANSI sequences have a width of zero. We only parse
 * a limited subset of ANSI here since we don't store ANSI codes in the
 * linenoiseState::buf, but we do encourage CSI color codes in prompts.
 */
static size_t GetMonospaceWidth(const char *p, size_t n, char *out_haswides) {
  int c, d;
  size_t i, w;
  struct rune r;
  char haswides;
  enum { kAscii, kUtf8, kEsc, kCsi1, kCsi2 } t;
  for (haswides = r.c = r.n = t = w = i = 0; i < n; ++i) {
    c = p[i] & 255;
    switch (t) {
    Whoopsie:
      t = kAscii;
        /* fallthrough */
      case kAscii:
        if (c < 0200) {
          if (c == 033) {
            t = kEsc;
          } else {
            ++w;
          }
        } else if (c >= 0300) {
          t = kUtf8;
          r = DecodeUtf8(c);
        }
        break;
      case kUtf8:
        if ((c & 0300) == 0200) {
          r.c <<= 6;
          r.c |= c & 077;
          if (!--r.n) {
            d = wcwidth(r.c);
            d = MAX(0, d);
            w += d;
            haswides |= d > 1;
            t = kAscii;
          }
        } else {
          goto Whoopsie;
        }
        break;
      case kEsc:
        if (c == '[') {
          t = kCsi1;
        } else {
          t = kAscii;
        }
        break;
      case kCsi1:
        if (0x20 <= c && c <= 0x2f) {
          t = kCsi2;
        } else if (0x40 <= c && c <= 0x7e) {
          t = kAscii;
        } else if (!(0x30 <= c && c <= 0x3f)) {
          goto Whoopsie;
        }
        break;
      case kCsi2:
        if (0x40 <= c && c <= 0x7e) {
          t = kAscii;
        } else if (!(0x20 <= c && c <= 0x2f)) {
          goto Whoopsie;
        }
        break;
      default:
        unreachable;
    }
  }
  if (out_haswides) {
    *out_haswides = haswides;
  }
  return w;
}

static void abInit(struct abuf *a) {
  a->len = 0;
  a->cap = 16;
  a->b = malloc(a->cap);
  a->b[0] = 0;
}

static char abGrow(struct abuf *a, int need) {
  int cap;
  char *b;
  cap = a->cap;
  do {
    cap += cap / 2;
  } while (cap < need);
  if (!(b = realloc(a->b, cap * sizeof(*a->b)))) return 0;
  a->cap = cap;
  a->b = b;
  return 1;
}

static void abAppend(struct abuf *a, const char *s, int len) {
  if (a->len + len + 1 > a->cap && !abGrow(a, a->len + len + 1)) return;
  memcpy(a->b + a->len, s, len);
  a->b[a->len + len] = 0;
  a->len += len;
}

static void abAppends(struct abuf *a, const char *s) {
  abAppend(a, s, strlen(s));
}

static void abAppendu(struct abuf *a, unsigned u) {
  char b[11];
  abAppend(a, b, FormatUnsigned(b, u) - b);
}

static void abAppendw(struct abuf *a, unsigned long long w) {
  char *p;
  if (a->len + 8 + 1 > a->cap && !abGrow(a, a->len + 8 + 1)) return;
  p = a->b + a->len;
  p[0] = (0x00000000000000ff & w) >> 000;
  p[1] = (0x000000000000ff00 & w) >> 010;
  p[2] = (0x0000000000ff0000 & w) >> 020;
  p[3] = (0x00000000ff000000 & w) >> 030;
  p[4] = (0x000000ff00000000 & w) >> 040;
  p[5] = (0x0000ff0000000000 & w) >> 050;
  p[6] = (0x00ff000000000000 & w) >> 060;
  p[7] = (0xff00000000000000 & w) >> 070;
  a->len += w ? (_bsrll(w) >> 3) + 1 : 1;
  p[8] = 0;
}

static void abFree(struct abuf *a) {
  free(a->b);
}

static void linenoiseUnpause(int fd) {
  if (ispaused) {
    tcflow(fd, TCOON);
    ispaused = 0;
  }
}

int linenoiseEnableRawMode(int fd) {
  struct termios raw;
  struct sigaction sa;
  if (rawmode == -1) {
    if (tcgetattr(fd, &orig_termios) != -1) {
      raw = orig_termios;
      raw.c_iflag &= ~(BRKINT | ICRNL | INPCK | ISTRIP | IXON);
      raw.c_lflag &= ~(ECHO | ICANON | IEXTEN | ISIG);
      raw.c_oflag |= OPOST | ONLCR;
      raw.c_iflag |= IUTF8;
      raw.c_cflag |= CS8;
      raw.c_cc[VMIN] = 1;
      raw.c_cc[VTIME] = 0;
      if (tcsetattr(fd, TCSANOW, &raw) != -1) {
        sa.sa_flags = 0;
        sa.sa_handler = linenoiseOnCont;
        sigemptyset(&sa.sa_mask);
        sigaction(SIGCONT, &sa, &orig_cont);
        sa.sa_handler = linenoiseOnWinch;
        sigaction(SIGWINCH, &sa, &orig_winch);
        rawmode = fd;
        gotwinch = 0;
        gotcont = 0;
        return 0;
      }
    }
    return enotty();
  } else {
    return 0;
  }
}

void linenoiseDisableRawMode(void) {
  if (rawmode != -1) {
    linenoiseUnpause(rawmode);
    sigaction(SIGCONT, &orig_cont, 0);
    sigaction(SIGWINCH, &orig_winch, 0);
    tcsetattr(rawmode, TCSANOW, &orig_termios);
    rawmode = -1;
  }
}

static int linenoiseWrite(int fd, const void *p, size_t n) {
  ssize_t rc;
  size_t wrote;
  do {
    for (;;) {
      if (ispaused) {
        return 0;
      }
      rc = write(fd, p, n);
      if (rc == -1 && errno == EINTR) {
        continue;
      } else {
        break;
      }
    }
    if (rc != -1) {
      wrote = rc;
      n -= wrote;
      p = (char *)p + wrote;
    } else {
      return -1;
    }
  } while (n);
  return 0;
}

static int linenoiseWriteStr(int fd, const char *p) {
  return linenoiseWrite(fd, p, strlen(p));
}

static void linenoiseDebug(struct linenoiseState *l, const char *fmt, ...) {
  va_list va;
  char *msg = 0;
  char *ansi = 0;
  int x, y, n, dy, xn;
  va_start(va, fmt);
  (vappendf)(&msg, fmt, va);
  va_end(va);
  xn = l->ws.ws_col;
  xn = MAX(xn, 80);
  y = l->debugrow;
  n = GetMonospaceWidth(msg, strlen(msg), 0);
  x = MAX(xn - n, 0);
  (appendf)(&ansi, "\e7\e[%u;%uH\e[1K%s\e8", y + 1, x + 1, msg);
  linenoiseWrite(l->ofd, ansi, appendz(ansi).i);
  y = (y + (n + (xn - 1)) / xn) % LINENOISE_MAX_DEBUG;
  l->debugrow = y;
  free(ansi);
  free(msg);
}

static int linenoisePoll(struct linenoiseState *l, int fd) {
  int rc;
  if ((rc = poll((struct pollfd[]){{fd, POLLIN}}, 1, 0))) {
    return rc;
  } else {
    l->dirty = true;
    return eagain();
  }
}

static ssize_t linenoiseRead(int fd, char *buf, size_t size,
                             struct linenoiseState *l, int block) {
  ssize_t rc;
  int refreshme;
  for (;;) {
    refreshme = 0;
    if (gotint) {
      errno = EINTR;
      return -1;
    }
    if (gotcont && rawmode != -1) {
      rawmode = -1;
      strace_enabled(-1);
      linenoiseEnableRawMode(0);
      strace_enabled(+1);
      if (l) refreshme = 1;
    }
    if (l && gotwinch) refreshme = 1;
    if (refreshme) linenoiseRefreshLine(l);
    if (!block && linenoisePoll(l, fd) == -1) return -1;
    strace_enabled(-1);
    rc = readansi(fd, buf, size);
    strace_enabled(+1);
    if (rc == -1 && errno == EINTR) {
      if (!block) break;
    } else {
      break;
    }
  }
  if (l && rc > 0) {
    memcpy(l->seq[1], l->seq[0], sizeof(l->seq[0]));
    memset(l->seq[0], 0, sizeof(l->seq[0]));
    memcpy(l->seq[0], buf, MIN(MIN(size, rc), sizeof(l->seq[0]) - 1));
  }
  return rc;
}

/**
 * Returns number of columns in current terminal.
 *
 * 1. Checks COLUMNS environment variable (set by Emacs)
 * 2. Tries asking termios (works for pseudoteletypewriters)
 * 3. Falls back to inband signalling (works w/ pipe or serial)
 * 4. Otherwise we conservatively assume 80 columns
 *
 * @param ws should be initialized by caller to zero before first call
 * @param ifd is input file descriptor
 * @param ofd is output file descriptor
 * @return window size
 */
struct winsize linenoiseGetTerminalSize(struct winsize ws, int ifd, int ofd) {
  int x;
  ssize_t n;
  char *p, *s, b[16];
  ioctl(ofd, TIOCGWINSZ, &ws);
  if ((!ws.ws_row && (s = getenv("ROWS")) && (x = ParseUnsigned(s, 0)))) {
    ws.ws_row = x;
  }
  if ((!ws.ws_col && (s = getenv("COLUMNS")) && (x = ParseUnsigned(s, 0)))) {
    ws.ws_col = x;
  }
  if (((!ws.ws_col || !ws.ws_row) && linenoiseRead(ifd, 0, 0, 0, 1) != -1 &&
       linenoiseWriteStr(
           ofd, "\e7"           /* save position */
                "\e[9979;9979H" /* move cursor to bottom right corner */
                "\e[6n"         /* report position */
                "\e8") != -1 && /* restore position */
       (n = linenoiseRead(ifd, b, sizeof(b), 0, 1)) != -1 &&
       n && b[0] == 033 && b[1] == '[' && b[n - 1] == 'R')) {
    p = b + 2;
    if ((x = ParseUnsigned(p, &p))) ws.ws_row = x;
    if (*p++ == ';' && (x = ParseUnsigned(p, 0))) ws.ws_col = x;
  }
  if (!ws.ws_col) ws.ws_col = 80;
  if (!ws.ws_row) ws.ws_row = 24;
  return ws;
}

/* Clear the screen. Used to handle ctrl+l */
void linenoiseClearScreen(int fd) {
  linenoiseWriteStr(fd, "\e[H"    /* move cursor to top left corner */
                        "\e[2J"); /* erase display */
}

static void linenoiseBeep(void) {
  // THE TERMINAL BELL IS DEAD - HISTORY HAS KILLED IT
}

static char linenoiseGrow(struct linenoiseState *ls, size_t n) {
  char *p;
  size_t m;
  m = ls->buflen;
  if (m >= n) return 1;
  do {
    m += m >> 1;
  } while (m < n);
  if (!(p = realloc(ls->buf, m * sizeof(*ls->buf)))) return 0;
  ls->buf = p;
  ls->buflen = m;
  return 1;
}

static wint_t ScrubCompletionCharacter(wint_t c) {
  if ((0x00 <= c && c <= 0x1F) || c == 0x7F) {
    return kCp437[c];
  } else {
    return c;
  }
}

static size_t linenoiseMaxCompletionWidth(linenoiseCompletions *lc) {
  size_t i, n, m;
  for (m = i = 0; i < lc->len; ++i) {
    n = GetMonospaceWidth(lc->cvec[i], strlen(lc->cvec[i]), 0);
    m = MAX(n, m);
  }
  return m;
}

static size_t Forward(struct linenoiseState *l, size_t pos) {
  return pos + GetUtf8(l->buf + pos, l->len - pos).n;
}

static size_t Backward(struct linenoiseState *l, size_t pos) {
  if (pos) {
    do {
      --pos;
    } while (pos && (l->buf[pos] & 0300) == 0200);
  }
  return pos;
}

static size_t Backwards(struct linenoiseState *l, size_t pos,
                        int pred(wint_t)) {
  size_t i;
  struct rune r;
  while (pos) {
    i = Backward(l, pos);
    r = GetUtf8(l->buf + i, l->len - i);
    if (pred(r.c)) {
      pos = i;
    } else {
      break;
    }
  }
  return pos;
}

static size_t Forwards(struct linenoiseState *l, size_t pos, int pred(wint_t)) {
  struct rune r;
  while (pos < l->len) {
    r = GetUtf8(l->buf + pos, l->len - pos);
    if (pred(r.c)) {
      pos += r.n;
    } else {
      break;
    }
  }
  return pos;
}

static size_t GetCommonPrefixLength(struct linenoiseCompletions *lc) {
  struct rune r;
  int i, j, n, c;
  i = 0;
  for (n = -1, i = 0; i < lc->len; ++i) {
    if (n != -1) {
      n = strnlen(lc->cvec[i], n);
    } else {
      n = strlen(lc->cvec[i]);
    }
  }
  for (i = 0, r.n = 0; i < n; i += r.n) {
    for (c = -1, j = 0; j < lc->len; ++j) {
      r = GetUtf8(lc->cvec[j] + i, n - i);
      if (c != -1) {
        if (r.c != c) {
          goto Finished;
        }
      } else {
        c = r.c;
      }
    }
  }
Finished:
  return i;
}

static void linenoiseEditHistoryGoto(struct linenoiseState *l, int i) {
  size_t n;
  if (historylen <= 1) return;
  i = MAX(MIN(i, historylen - 1), 0);
  free(history[historylen - 1 - l->hindex]);
  history[historylen - 1 - l->hindex] = strdup(l->buf);
  l->hindex = i;
  n = strlen(history[historylen - 1 - l->hindex]);
  linenoiseGrow(l, n + 1);
  n = MIN(n, l->buflen - 1);
  memcpy(l->buf, history[historylen - 1 - l->hindex], n);
  l->buf[n] = 0;
  l->len = l->pos = n;
  linenoiseRefreshLine(l);
}

static void linenoiseEditHistoryMove(struct linenoiseState *l, int dx) {
  linenoiseEditHistoryGoto(l, l->hindex + dx);
}

static char *linenoiseMakeSearchPrompt(int fail, const char *s, int n) {
  struct abuf ab;
  abInit(&ab);
  abAppendw(&ab, '(');
  if (fail) abAppends(&ab, "failed ");
  abAppends(&ab, "reverse-i-search `\e[4m");
  abAppend(&ab, s, n);
  abAppends(&ab, "\e[24m");
  abAppends(&ab, s + n);
  abAppendw(&ab, READ32LE("') "));
  return ab.b;
}

static void linenoiseRingFree(void) {
  size_t i;
  for (i = 0; i < LINENOISE_MAX_RING; ++i) {
    if (ring.p[i]) {
      free(ring.p[i]);
      ring.p[i] = 0;
    }
  }
}

static void linenoiseRingPush(const char *p, size_t n) {
  char *q;
  if (LINENOISE_MAX_RING && n) {
    if ((q = malloc(n + 1))) {
      ring.i = (ring.i + 1) % LINENOISE_MAX_RING;
      free(ring.p[ring.i]);
      ring.p[ring.i] = memcpy(q, p, n);
      ring.p[ring.i][n] = 0;
    }
  }
}

static void linenoiseRingRotate(void) {
  size_t i;
  for (i = 0; i < LINENOISE_MAX_RING; ++i) {
    ring.i = (ring.i - 1) % LINENOISE_MAX_RING;
    if (ring.p[ring.i]) {
      break;
    }
  }
}

static char *linenoiseRefreshHints(struct linenoiseState *l) {
  char *hint;
  struct abuf ab;
  const char *ansi1, *ansi2;
  if (!hintsCallback) return 0;
  if (!(hint = hintsCallback(l->buf, &ansi1, &ansi2))) return 0;
  abInit(&ab);
  ansi1 = "\e[90m";
  ansi2 = "\e[39m";
  if (ansi1) abAppends(&ab, ansi1);
  abAppends(&ab, hint);
  if (ansi2) abAppends(&ab, ansi2);
  if (freeHintsCallback) freeHintsCallback(hint);
  return ab.b;
}

static int linenoiseMirrorLeft(struct linenoiseState *l, unsigned res[2]) {
  unsigned c, pos, left, right, depth, index;
  if ((pos = Backward(l, l->pos))) {
    right = GetUtf8(l->buf + pos, l->len - pos).c;
    if ((left = GetMirrorLeft(right))) {
      depth = 0;
      index = pos;
      do {
        pos = Backward(l, pos);
        c = GetUtf8(l->buf + pos, l->len - pos).c;
        if (c == right) {
          ++depth;
        } else if (c == left) {
          if (depth) {
            --depth;
          } else {
            res[0] = pos;
            res[1] = index;
            return 0;
          }
        }
      } while (pos);
    }
  }
  return -1;
}

static int linenoiseMirrorRight(struct linenoiseState *l, unsigned res[2]) {
  struct rune rune;
  unsigned pos, left, right, depth, index;
  pos = l->pos;
  rune = GetUtf8(l->buf + pos, l->len - pos);
  left = rune.c;
  if ((right = GetMirrorRight(left))) {
    depth = 0;
    index = pos;
    do {
      pos += rune.n;
      rune = GetUtf8(l->buf + pos, l->len - pos);
      if (rune.c == left) {
        ++depth;
      } else if (rune.c == right) {
        if (depth) {
          --depth;
        } else {
          res[0] = index;
          res[1] = pos;
          return 0;
        }
      }
    } while (pos + rune.n < l->len);
  }
  return -1;
}

static int linenoiseMirror(struct linenoiseState *l, unsigned res[2]) {
  int rc;
  rc = linenoiseMirrorLeft(l, res);
  if (rc == -1) rc = linenoiseMirrorRight(l, res);
  return rc;
}

static void linenoiseRefreshLineImpl(struct linenoiseState *l, int force,
                                     const char *prefix) {
  char *hint;
  char flipit;
  char hasflip;
  char haswides;
  struct abuf ab;
  struct rune rune;
  unsigned flip[2];
  const char *p, *buf;
  struct winsize oldsize;
  int i, x, y, t, xn, yn, cx, cy, tn, resized;
  int fd, plen, width, pwidth, rows, len, pos;

  /*
   * synchonize the i/o state
   */
  if (ispaused) {
    if (force) {
      linenoiseUnpause(l->ofd);
    } else {
      return;
    }
  }
  if (!force && HasPendingInput(l->ifd)) {
    l->dirty = 1;
    return;
  }
  oldsize = l->ws;
  if ((resized = gotwinch) && rawmode != -1) {
    gotwinch = 0;
    l->ws = linenoiseGetTerminalSize(l->ws, l->ifd, l->ofd);
  }
  hasflip = !l->final && !linenoiseMirror(l, flip);

StartOver:
  fd = l->ofd;
  buf = l->buf;
  pos = l->pos;
  len = l->len;
  xn = l->ws.ws_col;
  yn = l->ws.ws_row;
  plen = strlen(l->prompt);
  pwidth = GetMonospaceWidth(l->prompt, plen, 0);
  width = GetMonospaceWidth(buf, len, &haswides);

  /*
   * handle the case where the line is larger than the whole display
   * gnu readline actually isn't able to deal with this situation!!!
   * we kludge xn to address the edge case of wide chars on the edge
   */
  for (tn = xn - haswides;;) {
    if (pwidth + width + 1 < tn * yn) break; /* we're fine */
    if (!len || width < 2) break;            /* we can't do anything */
    if (pwidth + 2 > tn * yn) break;         /* we can't do anything */
    if (pos > len / 2) {
      /* hide content on the left if we're editing on the right */
      rune = GetUtf8(buf, len);
      buf += rune.n;
      len -= rune.n;
      pos -= rune.n;
    } else {
      /* hide content on the right if we're editing on left */
      t = len;
      while (len && (buf[len - 1] & 0300) == 0200) --len;
      if (len) --len;
      rune = GetUtf8(buf + len, t - len);
    }
    if ((t = wcwidth(rune.c)) > 0) {
      width -= t;
    }
  }

  pos = MAX(0, MIN(pos, len));

  /*
   * now generate the terminal codes to update the line
   *
   * since we support unlimited lines it's important that we don't
   * clear the screen before we draw the screen. doing that causes
   * flickering. the key with terminals is to overwrite cells, and
   * then use \e[K and \e[J to clear everything else.
   *
   * we make the assumption that prompts and hints may contain ansi
   * sequences, but the buffer does not.
   *
   * we need to handle the edge case where a wide character like åº¦
   * might be at the edge of the window, when there's one cell left.
   * so we can't use division based on string width to compute the
   * coordinates and have to track it as we go.
   */
  cy = -1;
  cx = -1;
  rows = 1;
  abInit(&ab);
  if (prefix) {
    // to prevent flicker with ctrl+l
    abAppends(&ab, prefix);
  }
  abAppendw(&ab, '\r'); /* start of line */
  if (l->rows - l->oldpos - 1 > 0) {
    abAppends(&ab, "\e[");
    abAppendu(&ab, l->rows - l->oldpos - 1);
    abAppendw(&ab, 'A'); /* cursor up clamped */
  }
  abAppends(&ab, l->prompt);
  x = pwidth;
  for (i = 0; i < len; i += rune.n) {
    rune = GetUtf8(buf + i, len - i);
    if (x && x + rune.n > xn) {
      if (cy >= 0) ++cy;
      if (x < xn) {
        abAppends(&ab, "\e[K"); /* clear line forward */
      }
      abAppends(&ab, "\r"   /* start of line */
                     "\n"); /* cursor down unclamped */
      ++rows;
      x = 0;
    }
    if (i == pos) {
      cy = 0;
      cx = x;
    }
    if (maskmode) {
      abAppendw(&ab, '*');
    } else {
      flipit = hasflip && (i == flip[0] || i == flip[1]);
      if (flipit) abAppendw(&ab, READ32LE("\e[1m"));
      abAppendw(&ab, _tpenc(rune.c));
      if (flipit) abAppendw(&ab, READ64LE("\e[22m\0\0"));
    }
    t = wcwidth(rune.c);
    t = MAX(0, t);
    x += t;
  }
  if (!l->final && (hint = linenoiseRefreshHints(l))) {
    if (GetMonospaceWidth(hint, strlen(hint), 0) < xn - x) {
      if (cx < 0) {
        cx = x;
      }
      abAppends(&ab, hint);
    }
    free(hint);
  }
  abAppendw(&ab, READ32LE("\e[J")); /* erase display forwards */

  /*
   * if we are at the very end of the screen with our prompt, we need to
   * emit a newline and move the prompt to the first column.
   */
  if (pos && pos == len && x >= xn) {
    abAppendw(&ab, READ32LE("\n\r\0"));
    ++rows;
  }

  /*
   * move cursor to right position
   */
  if (cy > 0) {
    abAppendw(&ab, READ32LE("\e[\0"));
    abAppendu(&ab, cy);
    abAppendw(&ab, 'A'); /* cursor up */
  }
  if (cx > 0) {
    abAppendw(&ab, READ32LE("\r\e["));
    abAppendu(&ab, cx);
    abAppendw(&ab, 'C'); /* cursor right */
  } else if (!cx) {
    abAppendw(&ab, '\r'); /* start */
  }

  /*
   * now get ready to progress state
   * we use a mostly correct kludge when the tty resizes
   */
  l->rows = rows;
  if (resized && oldsize.ws_col > l->ws.ws_col) {
    resized = 0;
    abFree(&ab);
    goto StartOver;
  }
  l->oldpos = MAX(0, cy);
  l->dirty = 0;

  /*
   * send codes to terminal
   */
  linenoiseWrite(fd, ab.b, ab.len);
  abFree(&ab);
}

void linenoiseRefreshLine(struct linenoiseState *l) {
  strace_enabled(-1);
  linenoiseRefreshLineImpl(l, 0, 0);
  strace_enabled(+1);
}

static void linenoiseRefreshLineForce(struct linenoiseState *l) {
  strace_enabled(-1);
  linenoiseRefreshLineImpl(l, 1, 0);
  strace_enabled(+1);
}

static void linenoiseEditInsert(struct linenoiseState *l, const char *p,
                                size_t n) {
  if (linenoiseGrow(l, l->len + n + 1)) {
    memmove(l->buf + l->pos + n, l->buf + l->pos, l->len - l->pos);
    memcpy(l->buf + l->pos, p, n);
    l->pos += n;
    l->len += n;
    l->buf[l->len] = 0;
    linenoiseRefreshLine(l);
  }
}

static void linenoiseEditHome(struct linenoiseState *l) {
  l->pos = 0;
  linenoiseRefreshLine(l);
}

static void linenoiseEditEnd(struct linenoiseState *l) {
  l->pos = l->len;
  linenoiseRefreshLine(l);
}

static void linenoiseEditUp(struct linenoiseState *l) {
  linenoiseEditHistoryMove(l, LINENOISE_HISTORY_PREV);
}

static void linenoiseEditDown(struct linenoiseState *l) {
  linenoiseEditHistoryMove(l, LINENOISE_HISTORY_NEXT);
}

static void linenoiseEditBof(struct linenoiseState *l) {
  linenoiseEditHistoryMove(l, LINENOISE_HISTORY_FIRST);
}

static void linenoiseEditEof(struct linenoiseState *l) {
  linenoiseEditHistoryMove(l, LINENOISE_HISTORY_LAST);
}

static void linenoiseEditRefresh(struct linenoiseState *l) {
  strace_enabled(-1);
  linenoiseRefreshLineImpl(l, 1,
                           "\e[H"     // move cursor to top left corner
                           "\e[2J");  // erase display
  strace_enabled(+1);
}

static size_t ForwardWord(struct linenoiseState *l, size_t pos) {
  pos = Forwards(l, pos, iswseparator);
  pos = Forwards(l, pos, notwseparator);
  return pos;
}

static size_t BackwardWord(struct linenoiseState *l, size_t pos) {
  pos = Backwards(l, pos, iswseparator);
  pos = Backwards(l, pos, notwseparator);
  return pos;
}

static size_t EscapeWord(struct linenoiseState *l) {
  size_t i, j;
  struct rune r;
  for (i = l->pos; i && i < l->len; i += r.n) {
    if (i < l->len) {
      r = GetUtf8(l->buf + i, l->len - i);
      if (iswseparator(r.c)) break;
    }
    if ((j = i)) {
      do {
        --j;
      } while (j && (l->buf[j] & 0300) == 0200);
      r = GetUtf8(l->buf + j, l->len - j);
      if (iswseparator(r.c)) break;
    }
  }
  return i;
}

static void linenoiseEditLeft(struct linenoiseState *l) {
  l->pos = Backward(l, l->pos);
  linenoiseRefreshLine(l);
}

static void linenoiseEditRight(struct linenoiseState *l) {
  if (l->pos == l->len) return;
  do {
    l->pos++;
  } while (l->pos < l->len && (l->buf[l->pos] & 0300) == 0200);
  linenoiseRefreshLine(l);
}

static void linenoiseEditLeftWord(struct linenoiseState *l) {
  l->pos = BackwardWord(l, l->pos);
  linenoiseRefreshLine(l);
}

static void linenoiseEditRightWord(struct linenoiseState *l) {
  l->pos = ForwardWord(l, l->pos);
  linenoiseRefreshLine(l);
}

static void linenoiseEditLeftExpr(struct linenoiseState *l) {
  unsigned mark[2];
  l->pos = Backwards(l, l->pos, isxseparator);
  if (!linenoiseMirrorLeft(l, mark)) {
    l->pos = mark[0];
  } else {
    l->pos = Backwards(l, l->pos, notwseparator);
  }
  linenoiseRefreshLine(l);
}

static void linenoiseEditRightExpr(struct linenoiseState *l) {
  unsigned mark[2];
  l->pos = Forwards(l, l->pos, isxseparator);
  if (!linenoiseMirrorRight(l, mark)) {
    l->pos = Forward(l, mark[1]);
  } else {
    l->pos = Forwards(l, l->pos, notwseparator);
  }
  linenoiseRefreshLine(l);
}

static void linenoiseEditDelete(struct linenoiseState *l) {
  size_t i;
  if (l->pos == l->len) return;
  i = Forward(l, l->pos);
  memmove(l->buf + l->pos, l->buf + i, l->len - i + 1);
  l->len -= i - l->pos;
  linenoiseRefreshLine(l);
}

static void linenoiseEditRubout(struct linenoiseState *l) {
  size_t i;
  if (!l->pos) return;
  i = Backward(l, l->pos);
  memmove(l->buf + i, l->buf + l->pos, l->len - l->pos + 1);
  l->len -= l->pos - i;
  l->pos = i;
  linenoiseRefreshLine(l);
}

static void linenoiseEditDeleteWord(struct linenoiseState *l) {
  size_t i;
  if (l->pos == l->len) return;
  i = ForwardWord(l, l->pos);
  linenoiseRingPush(l->buf + l->pos, i - l->pos);
  memmove(l->buf + l->pos, l->buf + i, l->len - i + 1);
  l->len -= i - l->pos;
  linenoiseRefreshLine(l);
}

static void linenoiseEditRuboutWord(struct linenoiseState *l) {
  size_t i;
  if (!l->pos) return;
  i = BackwardWord(l, l->pos);
  linenoiseRingPush(l->buf + i, l->pos - i);
  memmove(l->buf + i, l->buf + l->pos, l->len - l->pos + 1);
  l->len -= l->pos - i;
  l->pos = i;
  linenoiseRefreshLine(l);
}

static void linenoiseEditXlatWord(struct linenoiseState *l,
                                  wint_t xlat(wint_t)) {
  int c;
  struct rune r;
  struct abuf ab;
  size_t i, j, p;
  abInit(&ab);
  i = Forwards(l, l->pos, iswseparator);
  for (j = i; j < l->len; j += r.n) {
    r = GetUtf8(l->buf + j, l->len - j);
    if (iswseparator(r.c)) break;
    if ((c = xlat(r.c)) != r.c) {
      abAppendw(&ab, _tpenc(c));
    } else { /* avoid canonicalization */
      abAppend(&ab, l->buf + j, r.n);
    }
  }
  if (ab.len && linenoiseGrow(l, i + ab.len + l->len - j + 1)) {
    l->pos = i + ab.len;
    abAppend(&ab, l->buf + j, l->len - j);
    l->len = i + ab.len;
    memcpy(l->buf + i, ab.b, ab.len + 1);
    linenoiseRefreshLine(l);
  }
  abFree(&ab);
}

static void linenoiseEditLowercaseWord(struct linenoiseState *l) {
  linenoiseEditXlatWord(l, towlower);
}

static void linenoiseEditUppercaseWord(struct linenoiseState *l) {
  linenoiseEditXlatWord(l, towupper);
}

static void linenoiseEditCapitalizeWord(struct linenoiseState *l) {
  iscapital = 0;
  linenoiseEditXlatWord(l, Capitalize);
}

static void linenoiseEditKillLeft(struct linenoiseState *l) {
  size_t diff, old_pos;
  linenoiseRingPush(l->buf, l->pos);
  old_pos = l->pos;
  l->pos = 0;
  diff = old_pos - l->pos;
  memmove(l->buf + l->pos, l->buf + old_pos, l->len - old_pos + 1);
  l->len -= diff;
  linenoiseRefreshLine(l);
}

static void linenoiseEditKillRight(struct linenoiseState *l) {
  linenoiseRingPush(l->buf + l->pos, l->len - l->pos);
  l->buf[l->pos] = '\0';
  l->len = l->pos;
  linenoiseRefreshLine(l);
}

static void linenoiseEditYank(struct linenoiseState *l) {
  char *p;
  size_t n;
  if (!ring.p[ring.i]) return;
  n = strlen(ring.p[ring.i]);
  linenoiseGrow(l, l->len + n + 1);
  p = malloc(l->len - l->pos + 1);
  memcpy(p, l->buf + l->pos, l->len - l->pos + 1);
  memcpy(l->buf + l->pos, ring.p[ring.i], n);
  memcpy(l->buf + l->pos + n, p, l->len - l->pos + 1);
  free(p);
  l->yi = l->pos;
  l->yj = l->pos + n;
  l->pos += n;
  l->len += n;
  linenoiseRefreshLine(l);
}

static void linenoiseEditRotate(struct linenoiseState *l) {
  if ((l->seq[1][0] == CTRL('Y') ||
       (l->seq[1][0] == '\e' && l->seq[1][1] == 'y'))) {
    if (l->yi < l->len && l->yj <= l->len) {
      memmove(l->buf + l->yi, l->buf + l->yj, l->len - l->yj + 1);
      l->len -= l->yj - l->yi;
      l->pos -= l->yj - l->yi;
    }
    linenoiseRingRotate();
    linenoiseEditYank(l);
  }
}

static void linenoiseEditTranspose(struct linenoiseState *l) {
  char *q, *p;
  size_t a, b, c;
  b = l->pos;
  a = Backward(l, b);
  c = Forward(l, b);
  if (!(a < b && b < c)) return;
  p = q = malloc(c - a);
  p = mempcpy(p, l->buf + b, c - b);
  p = mempcpy(p, l->buf + a, b - a);
  _unassert(p - q == c - a);
  memcpy(l->buf + a, q, p - q);
  l->pos = c;
  free(q);
  linenoiseRefreshLine(l);
}

static void linenoiseEditTransposeWords(struct linenoiseState *l) {
  char *q, *p;
  struct rune r;
  size_t pi, xi, xj, yi, yj;
  pi = EscapeWord(l);
  xj = Backwards(l, pi, iswseparator);
  xi = Backwards(l, xj, notwseparator);
  yi = Forwards(l, pi, iswseparator);
  yj = Forwards(l, yi, notwseparator);
  if (!(xi < xj && xj < yi && yi < yj)) return;
  p = q = malloc(yj - xi);
  p = mempcpy(p, l->buf + yi, yj - yi);
  p = mempcpy(p, l->buf + xj, yi - xj);
  p = mempcpy(p, l->buf + xi, xj - xi);
  _unassert(p - q == yj - xi);
  memcpy(l->buf + xi, q, p - q);
  l->pos = yj;
  free(q);
  linenoiseRefreshLine(l);
}

static void linenoiseEditSqueeze(struct linenoiseState *l) {
  size_t i, j;
  i = Backwards(l, l->pos, iswseparator);
  j = Forwards(l, l->pos, iswseparator);
  if (!(i < j)) return;
  memmove(l->buf + i, l->buf + j, l->len - j + 1);
  l->len -= j - i;
  l->pos = i;
  linenoiseRefreshLine(l);
}

static void linenoiseEditMark(struct linenoiseState *l) {
  l->mark = l->pos;
}

static void linenoiseEditGoto(struct linenoiseState *l) {
  if (l->mark > l->len) return;
  l->pos = MIN(l->mark, l->len);
  linenoiseRefreshLine(l);
}

static size_t linenoiseEscape(char *d, const char *s, size_t n) {
  char *p;
  size_t i;
  unsigned c, w, l;
  for (p = d, l = i = 0; i < n; ++i) {
    switch ((c = s[i] & 255)) {
      CASE('\e', w = READ16LE("\\e"));
      CASE('\a', w = READ16LE("\\a"));
      CASE('\b', w = READ16LE("\\b"));
      CASE('\t', w = READ16LE("\\t"));
      CASE('\n', w = READ16LE("\\n"));
      CASE('\v', w = READ16LE("\\v"));
      CASE('\f', w = READ16LE("\\f"));
      CASE('\r', w = READ16LE("\\r"));
      CASE('"', w = READ16LE("\\\""));
      CASE('\'', w = READ16LE("\\\'"));
      CASE('\\', w = READ16LE("\\\\"));
      default:
        if ((0x00 <= c && c <= 0x1F) || c == 0x7F || (c == '?' && l == '?')) {
          w = READ16LE("\\x");
          w |= "0123456789abcdef"[(c & 0xF0) >> 4] << 020;
          w |= "0123456789abcdef"[(c & 0x0F) >> 0] << 030;
        } else {
          w = c;
        }
        break;
    }
    WRITE32LE(p, w);
    p += (_bsr(w) >> 3) + 1;
    l = w;
  }
  return p - d;
}

static void linenoiseEditInterrupt(struct linenoiseState *l) {
  gotint = SIGINT;
}

static void linenoiseEditQuit(struct linenoiseState *l) {
  gotint = SIGQUIT;
}

static void linenoiseEditSuspend(struct linenoiseState *l) {
  raise(SIGSTOP);
}

static void linenoiseEditPause(struct linenoiseState *l) {
  tcflow(l->ofd, TCOOFF);
  ispaused = 1;
}

static void linenoiseEditCtrlq(struct linenoiseState *l) {
}

/**
 * Moves last item inside current s-expression to outside, e.g.
 *
 *     (a| b c)
 *     (a| b) c
 *
 * The cursor position changes only if a paren is moved before it:
 *
 *     (a b    c   |)
 *     (a b)    c   |
 *
 * To accommodate non-LISP languages we connect unspaced outer symbols:
 *
 *     f(a,| b, g())
 *     f(a,| b), g()
 *
 * Our standard keybinding is ALT-SHIFT-B.
 */
static void linenoiseEditBarf(struct linenoiseState *l) {
  struct rune r;
  unsigned long w;
  size_t i, pos, depth = 0;
  unsigned lhs, rhs, end, *stack = 0;
  /* go as far right within current s-expr as possible */
  for (pos = l->pos;; pos += r.n) {
    if (pos == l->len) goto Finish;
    r = GetUtf8(l->buf + pos, l->len - pos);
    if (depth) {
      if (r.c == stack[depth - 1]) {
        --depth;
      }
    } else {
      if ((rhs = GetMirrorRight(r.c))) {
        stack = (unsigned *)realloc(stack, ++depth * sizeof(*stack));
        stack[depth - 1] = rhs;
      } else if (GetMirrorLeft(r.c)) {
        end = pos;
        break;
      }
    }
  }
  /* go back one item */
  pos = Backwards(l, pos, isxseparator);
  for (;; pos = i) {
    if (!pos) goto Finish;
    i = Backward(l, pos);
    r = GetUtf8(l->buf + i, l->len - i);
    if (depth) {
      if (r.c == stack[depth - 1]) {
        --depth;
      }
    } else {
      if ((lhs = GetMirrorLeft(r.c))) {
        stack = (unsigned *)realloc(stack, ++depth * sizeof(*stack));
        stack[depth - 1] = lhs;
      } else if (iswseparator(r.c)) {
        break;
      }
    }
  }
  pos = Backwards(l, pos, isxseparator);
  /* now move the text */
  r = GetUtf8(l->buf + end, l->len - end);
  memmove(l->buf + pos + r.n, l->buf + pos, end - pos);
  w = _tpenc(r.c);
  for (i = 0; i < r.n; ++i) {
    l->buf[pos + i] = w;
    w >>= 8;
  }
  if (l->pos > pos) {
    l->pos += r.n;
  }
  linenoiseRefreshLine(l);
Finish:
  free(stack);
}

/**
 * Moves first item outside current s-expression to inside, e.g.
 *
 *     (a| b) c d
 *     (a| b c) d
 *
 * To accommodate non-LISP languages we connect unspaced outer symbols:
 *
 *     f(a,| b), g()
 *     f(a,| b, g())
 *
 * Our standard keybinding is ALT-SHIFT-S.
 */
static void linenoiseEditSlurp(struct linenoiseState *l) {
  char rp[6];
  struct rune r;
  size_t pos, depth = 0;
  unsigned rhs, point = 0, start = 0, *stack = 0;
  /* go to outside edge of current s-expr */
  for (pos = l->pos; pos < l->len; pos += r.n) {
    r = GetUtf8(l->buf + pos, l->len - pos);
    if (depth) {
      if (r.c == stack[depth - 1]) {
        --depth;
      }
    } else {
      if ((rhs = GetMirrorRight(r.c))) {
        stack = (unsigned *)realloc(stack, ++depth * sizeof(*stack));
        stack[depth - 1] = rhs;
      } else if (GetMirrorLeft(r.c)) {
        point = pos;
        pos += r.n;
        start = pos;
        break;
      }
    }
  }
  /* go forward one item */
  pos = Forwards(l, pos, isxseparator);
  for (; pos < l->len; pos += r.n) {
    r = GetUtf8(l->buf + pos, l->len - pos);
    if (depth) {
      if (r.c == stack[depth - 1]) {
        --depth;
      }
    } else {
      if ((rhs = GetMirrorRight(r.c))) {
        stack = (unsigned *)realloc(stack, ++depth * sizeof(*stack));
        stack[depth - 1] = rhs;
      } else if (iswseparator(r.c)) {
        break;
      }
    }
  }
  /* now move the text */
  memcpy(rp, l->buf + point, start - point);
  memmove(l->buf + point, l->buf + start, pos - start);
  memcpy(l->buf + pos - (start - point), rp, start - point);
  linenoiseRefreshLine(l);
  free(stack);
}

struct linenoiseState *linenoiseBegin(const char *prompt, int ifd, int ofd) {
  struct linenoiseState *l;
  if (!(l = calloc(1, sizeof(*l)))) {
    return 0;
  }
  if (!(l->buf = malloc((l->buflen = 32)))) {
    free(l);
    return 0;
  }
  l->state = DUFF_ROUTINE_START;
  l->buf[0] = 0;
  l->ifd = ifd;
  l->ofd = ofd;
  l->prompt = strdup(prompt ? prompt : "");
  l->ws = linenoiseGetTerminalSize(l->ws, l->ifd, l->ofd);
  linenoiseWriteStr(l->ofd, l->prompt);
  abInit(&l->ab);
  return l;
}

void linenoiseReset(struct linenoiseState *l) {
  l->buf[0] = 0;
  l->dirty = true;
  l->final = 0;
  l->hindex = 0;
  l->len = 0;
  l->mark = 0;
  l->oldpos = 0;
  l->pos = 0;
  l->yi = 0;
  l->yj = 0;
}

void linenoiseEnd(struct linenoiseState *l) {
  if (l) {
    linenoiseFreeCompletions(&l->lc);
    abFree(&l->ab);
    free(l->oldprompt);
    free(l->prompt);
    free(l->buf);
    free(l);
  }
}

static int CompareStrings(const void *a, const void *b) {
  return strcmp(*(const char **)a, *(const char **)b);
}

/**
 * Runs linenoise engine.
 *
 * This function is the core of the line editing capability of linenoise.
 * It expects 'fd' to be already in "raw mode" so that every key pressed
 * will be returned ASAP to read(). The exit conditions are:
 *
 * 1. ret >  0 / buf â  0 / errno = ? -- means we got some
 * 2. ret =  0 / buf â  0 / errno = ? -- means empty line
 * 3. ret =  0 / buf = 0 / errno = ? -- means eof
 * 4. ret = -1 / buf = ? / errno â  0 -- means error
 *
 * @param l is linenoise reader object created by linenoiseBegin()
 * @param prompt if non-null is copied and replaces current prompt
 * @param block if false will cause -1 / EAGAIN if there's no data
 * @return chomped character count in buf >=0 or -1 on eof / error
 */
ssize_t linenoiseEdit(struct linenoiseState *l, const char *prompt, char **obuf,
                      bool block) {
  ssize_t rc;
  char seq[16];

  gotint = 0;
  if (prompt && l->state != DUFF_ROUTINE_SEARCH &&
      (!l->prompt || strcmp(prompt, l->prompt))) {
    free(l->prompt);
    l->prompt = strdup(prompt);
  }

  switch (l->state) {
    for (;;) {
      DUFF_ROUTINE_READ(DUFF_ROUTINE_LOOP);
    HandleRead:
      if (!rc && l->len) {
        rc = 1;
        seq[0] = '\r';
        seq[1] = 0;
      } else if (!rc || rc == -1) {
        free(history[--historylen]);
        history[historylen] = 0;
        linenoiseReset(l);
        if (!rc) *obuf = 0;
        return rc;
      }

      // handle reverse history search
      if (seq[0] == CTRL('R')) {
        int fail, added, oldpos;
        if (historylen <= 1) continue;
        l->ab.len = 0;
        l->olderpos = l->pos;
        l->oldprompt = l->prompt;
        l->oldindex = l->hindex;
        l->prompt = 0;
        for (fail = l->matlen = 0;;) {
          free(l->prompt);
          l->prompt = linenoiseMakeSearchPrompt(fail, l->ab.b, l->matlen);
          DUFF_ROUTINE_READ(DUFF_ROUTINE_SEARCH);
          fail = 1;
          added = 0;
          l->j = l->pos;
          l->i = l->hindex;
          if (rc > 0) {
            if (seq[0] == CTRL('?') || seq[0] == CTRL('H')) {
              if (l->ab.len) {
                --l->ab.len;
                l->matlen = MIN(l->matlen, l->ab.len);
              }
            } else if (seq[0] == CTRL('R')) {
              if (l->j) {
                --l->j;
              } else if (l->i + 1 < historylen) {
                ++l->i;
                l->j = strlen(history[historylen - 1 - l->i]);
              }
            } else if (seq[0] == CTRL('G')) {
              linenoiseEditHistoryGoto(l, l->oldindex);
              l->pos = l->olderpos;
              break;
            } else if (iswcntrl(seq[0])) {  // only sees canonical c0
              break;
            } else {
              abAppend(&l->ab, seq, rc);
              added = rc;
            }
          } else {
            break;
          }
          while (l->i < historylen) {
            int k;
            char *p;
            const char *q;
            p = history[historylen - 1 - l->i];
            k = strlen(p);
            l->j = l->j >= 0 ? MIN(k, l->j + l->ab.len) : k;
            if ((q = FindSubstringReverse(p, l->j, l->ab.b, l->ab.len))) {
              linenoiseEditHistoryGoto(l, l->i);
              l->pos = q - p;
              fail = 0;
              if (added) {
                l->matlen += added;
                added = 0;
              }
              break;
            } else {
              l->i = l->i + 1;
              l->j = -1;
            }
          }
        }
        free(l->prompt);
        l->prompt = l->oldprompt;
        l->oldprompt = 0;
        linenoiseRefreshLine(l);
        goto HandleRead;
      }

      // handle tab and tab-tab completion
      if (seq[0] == '\t' && completionCallback) {
        size_t i, n, m;
        // we know that the user pressed tab once
        rc = 0;
        linenoiseFreeCompletions(&l->lc);
        i = Backwards(l, l->pos, iswname);
        {
          char *s = strndup(l->buf + i, l->pos - i);
          completionCallback(s, &l->lc);
          free(s);
        }
        m = GetCommonPrefixLength(&l->lc);
        if (m > l->pos - i || (m == l->pos - i && l->lc.len == 1)) {
          // on common prefix (or single completion) we complete and return
          n = i + m + (l->len - l->pos);
          if (linenoiseGrow(l, n + 1)) {
            memmove(l->buf + i + m, l->buf + l->pos, l->len - l->pos + 1);
            memcpy(l->buf + i, l->lc.cvec[0], m);
            l->pos = i + m;
            l->len = n;
          }
          continue;
        }
        if (l->lc.len > 1) {
          qsort(l->lc.cvec, l->lc.len, sizeof(*l->lc.cvec), CompareStrings);
          // if there's a multiline completions, then do nothing and wait and
          // see if the user presses tab again. if the user does this we then
          // print ALL the completions, to above the editing line
          for (i = 0; i < l->lc.len; ++i) {
            char *s = l->lc.cvec[i];
            l->lc.cvec[i] = VisualizeControlCodes(s, -1, 0);
            free(s);
          }
          for (;;) {
            DUFF_ROUTINE_READ(2);
            if (rc == 1 && seq[0] == '\t') {
              const char **p;
              struct abuf ab;
              int i, k, x, y, xn, yn, xy, itemlen;
              itemlen = linenoiseMaxCompletionWidth(&l->lc) + 4;
              xn = MAX(1, (l->ws.ws_col - 1) / itemlen);
              yn = (l->lc.len + (xn - 1)) / xn;
              if (!__builtin_mul_overflow(xn, yn, &xy) &&
                  (p = calloc(xy, sizeof(char *)))) {
                // arrange in column major order
                for (i = x = 0; x < xn; ++x) {
                  for (y = 0; y < yn; ++y) {
                    p[y * xn + x] = i < l->lc.len ? l->lc.cvec[i++] : "";
                  }
                }
                abInit(&ab);
                abAppends(&ab, "\r\n\e[K");
                for (x = i = 0; i < xy; ++i) {
                  n = GetMonospaceWidth(p[i], strlen(p[i]), 0);
                  abAppends(&ab, p[i]);
                  for (k = n; k < itemlen; ++k) {
                    abAppendw(&ab, ' ');
                  }
                  if (++x == xn) {
                    abAppendw(&ab, READ16LE("\r\n"));
                    x = 0;
                  }
                }
                ab.len -= 2;
                abAppends(&ab, "\n");
                linenoiseWriteStr(l->ofd, ab.b);
                linenoiseRefreshLine(l);
                abFree(&ab);
                free(p);
              }
            } else {
              goto HandleRead;
            }
          }
        }
      }

      // handle (1) emacs keyboard combos
      //        (2) otherwise sigint exit
      if (seq[0] == CTRL('C')) {
        DUFF_ROUTINE_READ(3);
        if (rc == 1) {
          switch (seq[0]) {
            CASE(CTRL('C'), linenoiseEditInterrupt(l));
            CASE(CTRL('B'), linenoiseEditBarf(l));
            CASE(CTRL('S'), linenoiseEditSlurp(l));
            default:
              goto HandleRead;
          }
          continue;
        } else {
          goto HandleRead;
        }
      }

      // handle (1) unpausing terminal after ctrl-s
      //        (2) otherwise raw keystroke inserts
      if (seq[0] == CTRL('Q')) {
        if (ispaused) {
          linenoiseUnpause(l->ofd);
        } else {
          DUFF_ROUTINE_READ(4);
          if (rc > 0) {
            char esc[sizeof(seq) * 4];
            size_t m = linenoiseEscape(esc, seq, rc);
            linenoiseEditInsert(l, esc, m);
          } else {
            goto HandleRead;
          }
        }
        continue;
      }

      // handle enter key
      if (seq[0] == '\r') {
        char *p;
        l->final = 1;
        free(history[--historylen]);
        history[historylen] = 0;
        linenoiseEditEnd(l);
        linenoiseRefreshLineForce(l);
        p = strdup(l->buf);
        linenoiseReset(l);
        if (p) {
          *obuf = p;
          l->state = DUFF_ROUTINE_START;
          return l->len;
        } else {
          return -1;
        }
        DUFF_ROUTINE_LABEL(DUFF_ROUTINE_START);
        linenoiseHistoryAdd("");
        continue;
      }

      // handle keystrokes that don't need read()
      switch (seq[0]) {
        CASE(CTRL('P'), linenoiseEditUp(l));
        CASE(CTRL('E'), linenoiseEditEnd(l));
        CASE(CTRL('N'), linenoiseEditDown(l));
        CASE(CTRL('A'), linenoiseEditHome(l));
        CASE(CTRL('B'), linenoiseEditLeft(l));
        CASE(CTRL('@'), linenoiseEditMark(l));
        CASE(CTRL('Y'), linenoiseEditYank(l));
        CASE(CTRL('F'), linenoiseEditRight(l));
        CASE(CTRL('\\'), linenoiseEditQuit(l));
        CASE(CTRL('S'), linenoiseEditPause(l));
        CASE(CTRL('?'), linenoiseEditRubout(l));
        CASE(CTRL('H'), linenoiseEditRubout(l));
        CASE(CTRL('L'), linenoiseEditRefresh(l));
        CASE(CTRL('Z'), linenoiseEditSuspend(l));
        CASE(CTRL('U'), linenoiseEditKillLeft(l));
        CASE(CTRL('T'), linenoiseEditTranspose(l));
        CASE(CTRL('K'), linenoiseEditKillRight(l));
        CASE(CTRL('W'), linenoiseEditRuboutWord(l));

        case CTRL('X'):
          if (l->seq[1][0] == CTRL('X')) {
            linenoiseEditGoto(l);
          }
          break;

        case CTRL('D'):
          if (l->len) {
            linenoiseEditDelete(l);
          } else {
            free(history[--historylen]);
            history[historylen] = 0;
            linenoiseReset(l);
            *obuf = 0;
            return 0;
          }
          break;

        case '\e':
          // handle ansi escape
          if (rc < 2) break;
          switch (seq[1]) {
            CASE('<', linenoiseEditBof(l));
            CASE('>', linenoiseEditEof(l));
            CASE('y', linenoiseEditRotate(l));
            CASE('\\', linenoiseEditSqueeze(l));
            CASE('b', linenoiseEditLeftWord(l));
            CASE('f', linenoiseEditRightWord(l));
            CASE('h', linenoiseEditRuboutWord(l));
            CASE('d', linenoiseEditDeleteWord(l));
            CASE('l', linenoiseEditLowercaseWord(l));
            CASE('u', linenoiseEditUppercaseWord(l));
            CASE('c', linenoiseEditCapitalizeWord(l));
            CASE('t', linenoiseEditTransposeWords(l));
            CASE(CTRL('B'), linenoiseEditLeftExpr(l));
            CASE(CTRL('F'), linenoiseEditRightExpr(l));
            CASE(CTRL('H'), linenoiseEditRuboutWord(l));

            case '[':
              // handle ansi csi sequences
              if (rc < 3) break;
              if (seq[2] >= '0' && seq[2] <= '9') {
                if (rc < 4) break;
                if (seq[3] == '~') {
                  switch (seq[2]) {
                    CASE('1', linenoiseEditHome(l));    // \e[1~
                    CASE('3', linenoiseEditDelete(l));  // \e[3~
                    CASE('4', linenoiseEditEnd(l));     // \e[4~
                    default:
                      break;
                  }
                } else if (rc == 6 && seq[2] == '1' && seq[3] == ';' &&
                           seq[4] == '5') {
                  switch (seq[5]) {
                    CASE('C', linenoiseEditRightWord(l));  // \e[1;5C ctrl-right
                    CASE('D', linenoiseEditLeftWord(l));   // \e[1;5D ctrl-left
                    default:
                      break;
                  }
                }
              } else {
                switch (seq[2]) {
                  CASE('A', linenoiseEditUp(l));
                  CASE('B', linenoiseEditDown(l));
                  CASE('C', linenoiseEditRight(l));
                  CASE('D', linenoiseEditLeft(l));
                  CASE('H', linenoiseEditHome(l));
                  CASE('F', linenoiseEditEnd(l));
                  default:
                    break;
                }
              }
              break;

            case 'O':
              if (rc < 3) break;
              switch (seq[2]) {
                CASE('A', linenoiseEditUp(l));
                CASE('B', linenoiseEditDown(l));
                CASE('C', linenoiseEditRight(l));
                CASE('D', linenoiseEditLeft(l));
                CASE('H', linenoiseEditHome(l));
                CASE('F', linenoiseEditEnd(l));
                default:
                  break;
              }
              break;

            case '\e':
              if (rc < 3) break;
              switch (seq[2]) {
                case '[':
                  if (rc < 4) break;
                  switch (seq[3]) {
                    CASE('C', linenoiseEditRightExpr(l));  // \e\e[C alt-right
                    CASE('D', linenoiseEditLeftExpr(l));   // \e\e[D alt-left
                    default:
                      break;
                  }
                  break;
                case 'O':
                  if (rc < 4) break;
                  switch (seq[3]) {
                    CASE('C', linenoiseEditRightExpr(l));  // \e\eOC alt-right
                    CASE('D', linenoiseEditLeftExpr(l));   // \e\eOD alt-left
                    default:
                      break;
                  }
                  break;
                default:
                  break;
              }
              break;
            default:
              break;
          }
          break;

        default:
          // handle normal keystrokes
          if (!iswcntrl(seq[0])) {  // only sees canonical c0
            if (xlatCallback) {
              uint64_t w;
              struct rune rune;
              rune = GetUtf8(seq, rc);
              w = _tpenc(xlatCallback(rune.c));
              rc = 0;
              do {
                seq[rc++] = w;
              } while ((w >>= 8));
            }
            linenoiseEditInsert(l, seq, rc);
          }
          break;
      }
    }
    default:
      unreachable;
  }
}

void linenoiseFree(void *ptr) {
  free(ptr);
}

void linenoiseHistoryFree(void) {
  size_t i;
  for (i = 0; i < LINENOISE_MAX_HISTORY; i++) {
    if (history[i]) {
      free(history[i]);
      history[i] = 0;
    }
  }
  historylen = 0;
}

int linenoiseHistoryAdd(const char *line) {
  char *linecopy;
  if (!LINENOISE_MAX_HISTORY) return 0;
  if (historylen && !strcmp(history[historylen - 1], line)) return 0;
  if (!(linecopy = strdup(line))) return 0;
  if (historylen == LINENOISE_MAX_HISTORY) {
    free(history[0]);
    memmove(history, history + 1, sizeof(char *) * (LINENOISE_MAX_HISTORY - 1));
    historylen--;
  }
  history[historylen++] = linecopy;
  return 1;
}

/**
 * Saves line editing history to file.
 *
 * @return 0 on success, or -1 w/ errno
 */
int linenoiseHistorySave(const char *filename) {
  int j;
  FILE *fp;
  mode_t old_umask;
  if (filename) {
    old_umask = umask(S_IXUSR | S_IRWXG | S_IRWXO);
    fp = fopen(filename, "w");
    umask(old_umask);
    if (!fp) return -1;
    chmod(filename, S_IRUSR | S_IWUSR);
    for (j = 0; j < historylen; j++) {
      fputs(history[j], fp);
      fputc('\n', fp);
    }
    fclose(fp);
  }
  return 0;
}

/**
 * Loads history from the specified file.
 *
 * If the file doesn't exist, zero is returned and this will do nothing.
 * If the file does exists and the operation succeeded zero is returned
 * otherwise on error -1 is returned.
 *
 * @return 0 on success, or -1 w/ errno
 */
int linenoiseHistoryLoad(const char *filename) {
  char **h;
  int rc, fd, err;
  size_t i, j, k, n, t;
  char *m, *e, *p, *q, *f, *s;
  err = errno, rc = 0;
  if (!LINENOISE_MAX_HISTORY) return 0;
  if (!(h = (char **)calloc(2 * LINENOISE_MAX_HISTORY, sizeof(char *))))
    return -1;
  if ((fd = open(filename, O_RDONLY)) != -1) {
    if ((n = GetFdSize(fd))) {
      if ((m = (char *)mmap(0, n, PROT_READ, MAP_SHARED, fd, 0)) !=
          MAP_FAILED) {
        for (i = 0, e = (p = m) + n; p < e; p = f + 1) {
          if (!(q = (char *)memchr(p, '\n', e - p))) q = e;
          for (f = q; q > p; --q) {
            if (q[-1] != '\n' && q[-1] != '\r') break;
          }
          if (q > p) {
            h[i * 2 + 0] = p;
            h[i * 2 + 1] = q;
            i = (i + 1) % LINENOISE_MAX_HISTORY;
          }
        }
        linenoiseHistoryFree();
        for (j = 0; j < LINENOISE_MAX_HISTORY; ++j) {
          if (h[(k = (i + j) % LINENOISE_MAX_HISTORY) * 2]) {
            if ((s = malloc((t = h[k * 2 + 1] - h[k * 2]) + 1))) {
              memcpy(s, h[k * 2], t), s[t] = 0;
              history[historylen++] = s;
            }
          }
        }
        munmap(m, n);
      } else {
        rc = -1;
      }
    }
    close(fd);
  } else if (errno == ENOENT) {
    errno = err;
  } else {
    rc = -1;
  }
  free(h);
  return rc;
}

/**
 * Returns appropriate system config location.
 * @return path needing free or null if prog is null
 */
char *linenoiseGetHistoryPath(const char *prog) {
  struct abuf path;
  const char *a, *b;
  if (!prog) return 0;
  if (strchr(prog, '/') || strchr(prog, '.')) return strdup(prog);
  abInit(&path);
  b = "";
  if (!(a = getenv("HOME"))) {
    if (!(a = getenv("HOMEDRIVE")) || !(b = getenv("HOMEPATH"))) {
      a = "";
    }
  }
  if (*a) {
    abAppends(&path, a);
    abAppends(&path, b);
    if (!_endswith(path.b, "/") && !_endswith(path.b, "\\")) {
      abAppendw(&path, '/');
    }
  }
  abAppendw(&path, '.');
  abAppends(&path, prog);
  abAppends(&path, "_history");
  return path.b;
}

/**
 * Reads line interactively.
 *
 * This function can be used instead of linenoise() in cases where we
 * know for certain we're dealing with a terminal, which means we can
 * avoid linking any stdio code.
 *
 * @return chomped allocated string of read line or null on eof/error
 */
char *linenoiseRaw(const char *prompt, int infd, int outfd) {
  char *buf;
  ssize_t rc;
  struct sigaction sa[3];
  struct linenoiseState *l;
  if (linenoiseEnableRawMode(infd) == -1) return 0;
  sigemptyset(&sa->sa_mask);
  sa->sa_flags = SA_NODEFER;
  sa->sa_handler = linenoiseOnInt;
  sigaction(SIGINT, sa, sa + 1);
  sigaction(SIGQUIT, sa, sa + 2);
  l = linenoiseBegin(prompt, infd, outfd);
  rc = linenoiseEdit(l, 0, &buf, true);
  linenoiseEnd(l);
  linenoiseDisableRawMode();
  sigaction(SIGQUIT, sa + 2, 0);
  sigaction(SIGINT, sa + 1, 0);
  if (gotint) {
    if (rc != -1) {
      free(buf);
    }
    raise(gotint);
    errno = EINTR;
    gotint = 0;
    rc = -1;
  }
  if (rc != -1) {
    if (buf) {
      linenoiseWriteStr(outfd, "\n");
    }
    return buf;
  } else {
    return 0;
  }
}

static int linenoiseFallback(const char *prompt, char **res) {
  if (prompt && *prompt &&
      (strchr(prompt, '\n') || strchr(prompt, '\t') ||
       strchr(prompt + 1, '\r'))) {
    errno = EINVAL;
    *res = 0;
    return 1;
  }
  if (!linenoiseIsTerminal()) {
    if (prompt && *prompt && linenoiseIsTeletype()) {
      fputs(prompt, stdout);
      fflush(stdout);
    }
    *res = linenoiseGetLine(stdin);
    return 1;
  } else {
    return 0;
  }
}

/**
 * Reads line intelligently.
 *
 * The high level function that is the main API of the linenoise library.
 * This function checks if the terminal has basic capabilities, just checking
 * for a blacklist of inarticulate terminals, and later either calls the line
 * editing function or uses dummy fgets() so that you will be able to type
 * something even in the most desperate of the conditions.
 *
 * @param prompt is printed before asking for input if we have a term
 *     and this may be set to empty or null to disable and prompt may
 *     contain ansi escape sequences, color, utf8, etc.
 * @return chomped allocated string of read line or null on eof/error
 */
char *linenoise(const char *prompt) {
  char *res;
  bool rm, rs;
  if (linenoiseFallback(prompt, &res)) return res;
  fflush(stdout);
  fflush(stdout);
  rm = __replmode;
  rs = __replstderr;
  __replmode = true;
  if (isatty(2)) __replstderr = true;
  res = linenoiseRaw(prompt, fileno(stdin), fileno(stdout));
  __replstderr = rs;
  __replmode = rm;
  return res;
}

/**
 * Reads line intelligently w/ history, e.g.
 *
 *     // see ~/.foo_history
 *     main() {
 *         char *line;
 *         while ((line = linenoiseWithHistory("IN> ", "foo"))) {
 *             printf("OUT> %s\n", line);
 *             free(line);
 *         }
 *     }
 *
 * @param prompt is printed before asking for input if we have a term
 *     and this may be set to empty or null to disable and prompt may
 *     contain ansi escape sequences, color, utf8, etc.
 * @param prog is name of your app, used to generate history filename
 *     however if it contains a slash / dot then we'll assume prog is
 *     the history filename which as determined by the caller
 * @return chomped allocated string of read line or null on eof/error
 *     noting that on eof your errno is not changed
 */
char *linenoiseWithHistory(const char *prompt, const char *prog) {
  char *path, *line, *res;
  if (linenoiseFallback(prompt, &res)) return res;
  fflush(stdout);
  if ((path = linenoiseGetHistoryPath(prog))) {
    if (linenoiseHistoryLoad(path) == -1) {
      fprintf(stderr, "%s: failed to load history: %m\n", path);
      free(path);
      path = 0;
    }
  }
  line = linenoise(prompt);
  if (path && line && *line) {
    /* history here is inefficient but helpful when the user has multiple
     * repls open at the same time, so history propagates between them */
    linenoiseHistoryLoad(path);
    linenoiseHistoryAdd(line);
    linenoiseHistorySave(path);
  }
  free(path);
  return line;
}

/**
 * Returns 0 otherwise SIGINT or SIGQUIT if interrupt was received.
 */
int linenoiseGetInterrupt(void) {
  return gotint;
}

/**
 * Registers tab completion callback.
 */
void linenoiseSetCompletionCallback(linenoiseCompletionCallback *fn) {
  completionCallback = fn;
}

/**
 * Registers hints callback.
 *
 * Register a hits function to be called to show hits to the user at the
 * right of the prompt.
 */
void linenoiseSetHintsCallback(linenoiseHintsCallback *fn) {
  hintsCallback = fn;
}

/**
 * Sets free hints callback.
 *
 * This registers a function to free the hints returned by the hints
 * callback registered with linenoiseSetHintsCallback().
 */
void linenoiseSetFreeHintsCallback(linenoiseFreeHintsCallback *fn) {
  freeHintsCallback = fn;
}

/**
 * Sets character translation callback.
 */
void linenoiseSetXlatCallback(linenoiseXlatCallback *fn) {
  xlatCallback = fn;
}

/**
 * Adds completion.
 *
 * This function is used by the callback function registered by the user
 * in order to add completion options given the input string when the
 * user typed <tab>. See the example.c source code for a very easy to
 * understand example.
 */
void linenoiseAddCompletion(linenoiseCompletions *lc, const char *str) {
  size_t len;
  char *copy, **cvec;
  if ((copy = malloc((len = strlen(str)) + 1))) {
    memcpy(copy, str, len + 1);
    if ((cvec = realloc(lc->cvec, (lc->len + 1) * sizeof(*lc->cvec)))) {
      lc->cvec = cvec;
      lc->cvec[lc->len++] = copy;
    } else {
      free(copy);
    }
  }
}

/**
 * Frees list of completion option populated by linenoiseAddCompletion().
 */
void linenoiseFreeCompletions(linenoiseCompletions *lc) {
  size_t i;
  if (lc->cvec) {
    for (i = 0; i < lc->len; i++) {
      free(lc->cvec[i]);
    }
    free(lc->cvec);
  }
  lc->cvec = 0;
  lc->len = 0;
}

/**
 * Enables "mask mode".
 *
 * When it is enabled, instead of the input that the user is typing, the
 * terminal will just display a corresponding number of asterisks, like
 * "****". This is useful for passwords and other secrets that should
 * not be displayed.
 *
 * @see linenoiseMaskModeDisable()
 */
void linenoiseMaskModeEnable(void) {
  maskmode = 1;
}

/**
 * Disables "mask mode".
 */
void linenoiseMaskModeDisable(void) {
  maskmode = 0;
}

static void linenoiseAtExit(void) {
  linenoiseDisableRawMode();
  linenoiseHistoryFree();
  linenoiseRingFree();
}

static textstartup void linenoiseInit() {
  atexit(linenoiseAtExit);
}

const void *const linenoiseCtor[] initarray = {
    linenoiseInit,
};
