patch 8.0.0693: no terminal emulator support

Problem:    No terminal emulator support.  Cannot properly run commands in the
            GUI.  Cannot run a job interactively with an ssh connection.
Solution:   Very early implementation of the :terminal command.  Includes
            libvterm converted to ANSI C.  Many parts still missing.
diff --git a/src/libvterm/bin/vterm-dump.c b/src/libvterm/bin/vterm-dump.c
new file mode 100644
index 0000000..84a957a
--- /dev/null
+++ b/src/libvterm/bin/vterm-dump.c
@@ -0,0 +1,231 @@
+/* Require getopt(3) */
+#define _XOPEN_SOURCE
+
+#include <stdio.h>
+#include <string.h>
+#define streq(a,b) (strcmp(a,b)==0)
+
+#include <errno.h>
+#include <fcntl.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <unistd.h>
+
+#include "vterm.h"
+
+static const char *special_begin = "{";
+static const char *special_end   = "}";
+
+static int parser_text(const char bytes[], size_t len, void *user)
+{
+  unsigned char *b = (unsigned char *)bytes;
+
+  int i;
+  for(i = 0; i < len; /* none */) {
+    if(b[i] < 0x20)        /* C0 */
+      break;
+    else if(b[i] < 0x80)   /* ASCII */
+      i++;
+    else if(b[i] < 0xa0)   /* C1 */
+      break;
+    else if(b[i] < 0xc0)   /* UTF-8 continuation */
+      break;
+    else if(b[i] < 0xe0) { /* UTF-8 2-byte */
+      /* 2-byte UTF-8 */
+      if(len < i+2) break;
+      i += 2;
+    }
+    else if(b[i] < 0xf0) { /* UTF-8 3-byte */
+      if(len < i+3) break;
+      i += 3;
+    }
+    else if(b[i] < 0xf8) { /* UTF-8 4-byte */
+      if(len < i+4) break;
+      i += 4;
+    }
+    else                   /* otherwise invalid */
+      break;
+  }
+
+  printf("%.*s", i, b);
+  return i;
+}
+
+/* 0     1      2      3       4     5      6      7      8      9      A      B      C      D      E      F    */
+static const char *name_c0[] = {
+  "NUL", "SOH", "STX", "ETX", "EOT", "ENQ", "ACK", "BEL", "BS",  "HT",  "LF",  "VT",  "FF",  "CR",  "LS0", "LS1",
+  "DLE", "DC1", "DC2", "DC3", "DC4", "NAK", "SYN", "ETB", "CAN", "EM",  "SUB", "ESC", "FS",  "GS",  "RS",  "US",
+};
+static const char *name_c1[] = {
+  NULL,  NULL,  "BPH", "NBH", NULL,  "NEL", "SSA", "ESA", "HTS", "HTJ", "VTS", "PLD", "PLU", "RI",  "SS2", "SS3",
+  "DCS", "PU1", "PU2", "STS", "CCH", "MW",  "SPA", "EPA", "SOS", NULL,  "SCI", "CSI", "ST",  "OSC", "PM",  "APC",
+};
+
+static int parser_control(unsigned char control, void *user)
+{
+  if(control < 0x20)
+    printf("%s%s%s", special_begin, name_c0[control], special_end);
+  else if(control >= 0x80 && control < 0xa0 && name_c1[control - 0x80])
+    printf("%s%s%s", special_begin, name_c1[control - 0x80], special_end);
+  else
+    printf("%sCONTROL 0x%02x%s", special_begin, control, special_end);
+
+  if(control == 0x0a)
+    printf("\n");
+  return 1;
+}
+
+static int parser_escape(const char bytes[], size_t len, void *user)
+{
+  if(bytes[0] >= 0x20 && bytes[0] < 0x30) {
+    if(len < 2)
+      return -1;
+    len = 2;
+  }
+  else {
+    len = 1;
+  }
+
+  printf("%sESC %.*s%s", special_begin, (int)len, bytes, special_end);
+
+  return len;
+}
+
+/* 0     1      2      3       4     5      6      7      8      9      A      B      C      D      E      F    */
+static const char *name_csi_plain[] = {
+  "ICH", "CUU", "CUD", "CUF", "CUB", "CNL", "CPL", "CHA", "CUP", "CHT", "ED",  "EL",  "IL",  "DL",  "EF",  "EA",
+  "DCH", "SSE", "CPR", "SU",  "SD",  "NP",  "PP",  "CTC", "ECH", "CVT", "CBT", "SRS", "PTX", "SDS", "SIMD",NULL,
+  "HPA", "HPR", "REP", "DA",  "VPA", "VPR", "HVP", "TBC", "SM",  "MC",  "HPB", "VPB", "RM",  "SGR", "DSR", "DAQ",
+};
+
+/*0           4           8           B         */
+static const int newline_csi_plain[] = {
+  0, 1, 1, 0, 0, 1, 1, 0, 1, 0, 0, 0, 0, 0, 0, 0,
+  0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0,
+  0, 0, 0, 0, 1, 1, 1, 0, 0, 0, 0, 0, 1, 0, 0, 0,
+};
+
+static int parser_csi(const char *leader, const long args[], int argcount, const char *intermed, char command, void *user)
+{
+  const char *name = NULL;
+  if(!leader && !intermed && command < 0x70)
+    name = name_csi_plain[command - 0x40];
+  else if(leader && streq(leader, "?") && !intermed) {
+    /* DEC */
+    switch(command) {
+      case 'h': name = "DECSM"; break;
+      case 'l': name = "DECRM"; break;
+    }
+    if(name)
+      leader = NULL;
+  }
+
+  if(!leader && !intermed && command < 0x70 && newline_csi_plain[command - 0x40])
+    printf("\n");
+
+  if(name)
+    printf("%s%s", special_begin, name);
+  else
+    printf("%sCSI", special_begin);
+
+  if(leader && leader[0])
+    printf(" %s", leader);
+
+  {
+    int i;
+    for(i = 0; i < argcount; i++) {
+      printf(i ? "," : " ");
+  }
+
+    if(args[i] == CSI_ARG_MISSING)
+      printf("*");
+    else {
+      while(CSI_ARG_HAS_MORE(args[i]))
+        printf("%ld+", CSI_ARG(args[i++]));
+      printf("%ld", CSI_ARG(args[i]));
+    }
+  }
+
+  if(intermed && intermed[0])
+    printf(" %s", intermed);
+
+  if(name)
+    printf("%s", special_end);
+  else
+    printf(" %c%s", command, special_end);
+
+  return 1;
+}
+
+static int parser_osc(const char *command, size_t cmdlen, void *user)
+{
+  printf("%sOSC %.*s%s", special_begin, (int)cmdlen, command, special_end);
+
+  return 1;
+}
+
+static int parser_dcs(const char *command, size_t cmdlen, void *user)
+{
+  printf("%sDCS %.*s%s", special_begin, (int)cmdlen, command, special_end);
+
+  return 1;
+}
+
+static VTermParserCallbacks parser_cbs = {
+  &parser_text, /* text */
+  &parser_control, /* control */
+  &parser_escape, /* escape */
+  &parser_csi, /* csi */
+  &parser_osc, /* osc */
+  &parser_dcs, /* dcs */
+  NULL /* resize */
+};
+
+int main(int argc, char *argv[])
+{
+  int use_colour = isatty(1);
+  const char *file;
+  int fd;
+  VTerm *vt;
+  int len;
+  char buffer[1024];
+
+  int opt;
+  while((opt = getopt(argc, argv, "c")) != -1) {
+    switch(opt) {
+      case 'c': use_colour = 1; break;
+    }
+  }
+
+  file = argv[optind++];
+
+  if(!file || streq(file, "-"))
+    fd = 0; /* stdin */
+  else {
+    fd = open(file, O_RDONLY);
+    if(fd == -1) {
+      fprintf(stderr, "Cannot open %s - %s\n", file, strerror(errno));
+      exit(1);
+    }
+  }
+
+  if(use_colour) {
+    special_begin = "\x1b[7m{";
+    special_end   = "}\x1b[m";
+  }
+
+  /* Size matters not for the parser */
+  vt = vterm_new(25, 80);
+  vterm_set_utf8(vt, 1);
+  vterm_parser_set_callbacks(vt, &parser_cbs, NULL);
+
+  while((len = read(fd, buffer, sizeof(buffer))) > 0) {
+    vterm_input_write(vt, buffer, len);
+  }
+
+  printf("\n");
+
+  close(fd);
+  vterm_free(vt);
+  return 0;
+}