chiark / gitweb /
@@@ more mess
authorMark Wooding <mdw@distorted.org.uk>
Sun, 7 Jul 2024 13:34:54 +0000 (14:34 +0100)
committerMark Wooding <mdw@distorted.org.uk>
Sun, 7 Jul 2024 13:35:09 +0000 (14:35 +0100)
configure.ac
debian/changelog
test/tvec-core.c
test/tvec-types.c
test/tvec.3.in
test/tvec.h
ui/tty.c [new file with mode: 0644]
ui/tty.h [new file with mode: 0644]
ui/ttycolour.h
utils/macros.h

index 416b25f12bfafe295de6ef15b7da4549893d9fad..2b7bcd7d2330450b03124c10f8c4469b7e52751b 100644 (file)
@@ -56,6 +56,7 @@ dnl Headers.
 AC_CHECK_HEADERS([float.h])
 AC_CHECK_HEADERS([stdint.h])
 AC_CHECK_HEADERS([valgrind/valgrind.h])
+AC_CHECK_HEADERS([wchar.h])
 
 dnl Libraries.
 mdw_ORIG_LIBS=$LIBS LIBS=$MLIB_LIBS
@@ -65,7 +66,9 @@ AC_SEARCH_LIBS([gethostbyname], [nsl resolv])
 MLIB_LIBS=$LIBS LIBS=$mdw_ORIG_LIBS
 
 dnl Functions.
+AC_CHECK_FUNCS([mbrtowc])
 AC_CHECK_FUNCS([snprintf])
+AC_CHECK_FUNCS([wcwidth])
 
 dnl Types.
 AC_CHECK_TYPE([socklen_t], [],
@@ -90,6 +93,106 @@ mdw_PROBE_FLTFMT
 dnl Set the master library list.
 AC_SUBST([MLIB_LIBS])
 
+dnl--------------------------------------------------------------------------
+dnl Terminal characteristics.
+
+mdw_ORIG_LIBS=$LIBS LIBS=$MLIB_LIBS
+
+AC_ARG_WITH([unibilium],
+  AS_HELP_STRING([--with-unibilium], [support `unibilium' library]),
+  [want_unibilium=$withval],
+  [want_unibilium=auto])
+
+case $want_unibilium in no) ;; *) have_unibilium=check ;; esac
+case $have_unibilium in
+  check)
+    PKG_CHECK_MODULES([unibilium], [unibilium >= 2.0.0],
+                     [], [have_unibilium=no])
+    ;;
+esac
+case $have_unibilium in check) have_unibilium=yes ;; esac
+
+case $want_unibilium,$have_unibilium in
+  yes,no)
+    AC_MSG_ERROR([`unibilium' library not found but explicitly requested])
+    ;;
+  yes,yes | auto,yes)
+    use_unibilium=yes
+    AC_DEFINE([HAVE_UNIBILIUM], [1],
+             [define if you have (and want to use) the `unibilium' library.])
+    ;;
+  no,* | auto,no)
+    use_unibilium=no
+    ;;
+esac
+AM_CONDITIONAL([WITH_UNIBILIUM], [test "$use_unibilium" = yes])
+
+AC_ARG_WITH([terminfo],
+  AS_HELP_STRING([--with-terminfo], [support `terminfo' library]),
+  [want_terminfo=$withval],
+  [want_terminfo=auto])
+
+case $want_terminfo in no) ;; *) have_terminfo=check ;; esac
+case $have_terminfo in
+  check) AC_CHECK_HEADERS([term.h], [], [have_terminfo=no]) ;;
+esac
+case $have_terminfo in
+  check)
+    AC_SEARCH_LIBS([tparm], [tinfo], [], [have_terminfo=no])
+    AC_CHECK_FUNCS([tiparm])
+    ;;
+esac
+case $have_terminfo in check) have_terminfo=yes ;; esac
+
+case $want_terminfo,$have_terminfo in
+  yes,no)
+    AC_MSG_ERROR([`terminfo' library not found but explicitly requested])
+    ;;
+  yes,yes | auto,yes)
+    use_terminfo=yes
+    AC_DEFINE([HAVE_TERMINFO], [1],
+             [define if you have (and want to use) the `terminfo' library.])
+    ;;
+  no,* | auto,no)
+    use_terminfo=no
+    ;;
+esac
+AM_CONDITIONAL([WITH_TERMINFO], [test "$use_terminfo" = yes])
+
+AC_ARG_WITH([termcap],
+  AS_HELP_STRING([--with-termcap], [support `termcap' library]),
+  [want_termcap=$withval],
+  [want_termcap=auto])
+
+case $want_termcap in no) ;; *) have_termcap=check ;; esac
+case $have_termcap in
+  check) AC_CHECK_HEADERS([termcap.h], [], [have_termcap=no]) ;;
+esac
+case $have_termcap in
+  check)
+    AC_SEARCH_LIBS([tgoto], [termcap], [], [have_termcap=no])
+    AC_SEARCH_LIBS([tgetstr], [termcap], [], [have_termcap=no])
+    ;;
+esac
+case $have_termcap in check) have_termcap=yes ;; esac
+
+case $want_termcap,$have_termcap in
+  yes,no)
+    AC_MSG_ERROR([`termcap' library not found but explicitly requested])
+    ;;
+  yes,yes | auto,yes)
+    use_termcap=yes
+    AC_DEFINE([HAVE_TERMCAP], [1],
+             [define if you have (and want to use) the `termcap' library.])
+    ;;
+  no,* | auto,no)
+    use_termcap=no
+    ;;
+esac
+AM_CONDITIONAL([WITH_TERMCAP], [test "$use_termcap" = yes])
+
+MLIB_LIBS=$LIBS LIBS=$mdw_ORIG_LIBS
+
 dnl--------------------------------------------------------------------------
 dnl Name resolution.
 
@@ -100,11 +203,16 @@ AC_ARG_WITH([adns],
   [want_adns=auto])
 
 mdw_ORIG_LIBS=$LIBS LIBS=$MLIB_LIBS
-case $want_adns in
-  no) ;;
-  *) AC_SEARCH_LIBS([adns_init], [adns], [have_adns=yes], [have_adns=no]) ;;
+
+case $want_adns in no) ;; *) have_adns=check ;; esac
+case $have_adns in
+  check) AC_CHECK_HEADERS([adns.h], [], [have_adns=no]) ;;
 esac
-MLIB_LIBS=$LIBS LIBS=$mdw_ORIG_LIBS
+case $have_adns in
+  check) AC_SEARCH_LIBS([adns_init], [adns], [], [have_adns=no]) ;;
+esac
+case $have_adns in check) have_adns=yes ;; esac
+
 case $want_adns,$have_adns in
   yes,no)
     AC_MSG_ERROR([ADNS library not found but explicitly requested])
@@ -125,6 +233,8 @@ case $want_adns,$have_adns in
 esac
 AM_CONDITIONAL([WITH_ADNS], [test "$use_adns" = yes])
 
+MLIB_LIBS=$LIBS LIBS=$mdw_ORIG_LIBS
+
 dnl--------------------------------------------------------------------------
 dnl Timers.
 
index a2fc30aad2b8627c421d8e8455ff7e682639ee58..26dc2e4072655eb59489ad695bbc4cdb2de1f575 100644 (file)
@@ -1,3 +1,9 @@
+mlib (2.5.99~) experimental; urgency=medium
+
+  * (placeholder for next minor version)
+
+ -- Mark Wooding <mdw@distorted.org.uk>  Tue, 21 May 2024 14:27:56 +0100
+
 mlib (2.5.0) experimental; urgency=medium
 
   * Fix internal Python build scripts to work with Python 3.
index 4c2ac5d3dcacaaf5d3655c0ce6cff667e43be725..6b54f0a9dd1e38eaac83dde0c0bb2ba529090fda 100644 (file)
@@ -390,15 +390,17 @@ int tvec_syntax_v(struct tvec_state *tv, int ch,
   dstr_destroy(&d); return (-1);
 }
 
-/* --- @tvec_unkregerr@ --- *
+/* --- @tvec_unkregerr@, @tvec_dupregerr@, @tvec_synthregerr@ --- *
  *
  * Arguments:  @struct tvec_state *tv@ = test-vector state
  *             @const char *name@ = register or pseudoregister name
  *
  * Returns:    %$-1$%.
  *
- * Use:                Reports an error that the register or pseudoregister is
- *             unrecognized.
+ * Use:                Reports an error about a misused register: @tvec_unkregerr@
+ *             reports that the register is unknown, @tvec_dupregerr@ that
+ *             it is already assigned, and @tvec_synthregerr@ that it is
+ *             synthetic.
  */
 
 int tvec_unkregerr(struct tvec_state *tv, const char *name)
@@ -407,19 +409,18 @@ int tvec_unkregerr(struct tvec_state *tv, const char *name)
                     name, tv->test->name));
 }
 
-/* --- @tvec_dupregerr@ --- *
- *
- * Arguments:  @struct tvec_state *tv@ = test-vector state
- *             @const char *name@ = register or pseudoregister name
- *
- * Returns:    %$-1$%.
- *
- * Use:                Reports an error that the register or pseudoregister has been
- *             assigned already in the current test.
- */
-
 int tvec_dupregerr(struct tvec_state *tv, const char *name)
-  { return (tvec_error(tv, "register `%s' is already set", name)); }
+{
+  return (tvec_error(tv, "register `%s' is already set in test `%s'",
+                    name, tv->test->name));
+}
+
+int tvec_synthregerr(struct tvec_state *tv, const char *name)
+{
+  return (tvec_error(tv, "register `%s' is synthetic in test `%s' "
+                    "and cannot be assigned",
+                    name, tv->test->name));
+}
 
 /* --- @tvec_skipspc@ --- *
  *
@@ -960,7 +961,7 @@ static void check(struct tvec_state *tv, struct groupstate *g)
     if (r->f&TVRF_LIVE) {
       if (rd->i < tv->cfg.nrout)
        TVEC_REG(tv, out, rd->i)->f |= TVRF_LIVE;
-    } else if (!(r->f&TVRF_SEEN) && !(rd->f&TVRF_OPT)) {
+    } else if (!(r->f&TVRF_SEEN) && !(rd->f&(TVRF_OPT | TVRF_SYNTH))) {
       tvec_error(tv, "required register `%s' not set in test `%s'",
                 rd->name, t->name);
       f |= f_err;
@@ -1297,6 +1298,8 @@ int tvec_read(struct tvec_state *tv, const char *infile, FILE *fp)
            r = TVEC_REG(tv, in, rd->i);
            if (r->f&TVRF_SEEN)
              { tvec_dupregerr(tv, rd->name); goto flush_line; }
+           else if (r->f&TVRF_SYNTH)
+             { tvec_synthregerr(tv, rd->name); goto flush_line; }
          }
 
          /* Now there should be a separator. */
index 481363cdb53ddcb46c7fcecb36480eba0cc5d7be..27381b5865ffb158c86586b93dcf32bc29509269 100644 (file)
@@ -1454,6 +1454,25 @@ static int eq_uint(const union tvec_regval *rv0,
                   const struct tvec_regdef *rd)
   { return (rv0->u == rv1->u); }
 
+/* --- @copy_int@, @copy_uint@ --- *
+ *
+ * Arguments:  @union tvec_regval *rvd@ = destination register value
+ *             @const union tvec_regval *rvs@ = source register value
+ *             @const struct tvec_regdef *rd@ = register definition
+ *
+ * Returns:    ---
+ *
+ * Use:                Copy a register value.
+ */
+
+static void copy_int(union tvec_regval *rvd, const union tvec_regval *rvs,
+                    const struct tvec_regdef *rd)
+  { rvd->i = rvs->i; }
+
+static void copy_uint(union tvec_regval *rvd, const union tvec_regval *rvs,
+                     const struct tvec_regdef *rd)
+  { rvd->u = rvs->u; }
+
 /* --- @tobuf_int@, @tobuf_uint@ --- *
  *
  * Arguments:  @buf *b@ = buffer
@@ -1610,12 +1629,12 @@ static void dump_uint(const union tvec_regval *rv,
 
 /* Integer type definitions. */
 const struct tvec_regty tvty_int = {
-  init_int, trivial_release, eq_int,
+  init_int, trivial_release, eq_int, copy_int,
   tobuf_int, frombuf_int,
   parse_int, dump_int
 };
 const struct tvec_regty tvty_uint = {
-  init_uint, trivial_release, eq_uint,
+  init_uint, trivial_release, eq_uint, copy_uint,
   tobuf_uint, frombuf_uint,
   parse_uint, dump_uint
 };
@@ -1755,7 +1774,7 @@ static void dump_size(const union tvec_regval *rv,
 
 /* Size type definitions. */
 const struct tvec_regty tvty_size = {
-  init_uint, trivial_release, eq_uint,
+  init_uint, trivial_release, eq_uint, copy_uint,
   tobuf_uint, frombuf_uint,
   parse_size, dump_size
 };
@@ -1823,6 +1842,21 @@ static int eq_float(const union tvec_regval *rv0,
                    const struct tvec_regdef *rd)
   { return (eqish_floating_p(rv1->f, rv0->f, rd->arg.p)); }
 
+/* --- @copy_float@ --- *
+ *
+ * Arguments:  @union tvec_regval *rvd@ = destination register value
+ *             @const union tvec_regval *rvs@ = source register value
+ *             @const struct tvec_regdef *rd@ = register definition
+ *
+ * Returns:    ---
+ *
+ * Use:                Copy a register value.
+ */
+
+static void copy_float(union tvec_regval *rvd, const union tvec_regval *rvs,
+                      const struct tvec_regdef *rd)
+  { rvd->f = rvs->f; }
+
 /* --- @tobuf_float@ --- *
  *
  * Arguments:  @buf *b@ = buffer
@@ -1926,7 +1960,7 @@ static void dump_float(const union tvec_regval *rv,
 
 /* Floating-point type definition. */
 const struct tvec_regty tvty_float = {
-  init_float, trivial_release, eq_float,
+  init_float, trivial_release, eq_float, copy_float,
   tobuf_float, frombuf_float,
   parse_float, dump_float
 };
@@ -2204,7 +2238,7 @@ static void dump_duration(const union tvec_regval *rv,
 
 /* Duration type definition. */
 const struct tvec_regty tvty_duration = {
-  init_float, trivial_release, eq_float,
+  init_float, trivial_release, eq_float, copy_float,
   tobuf_float, frombuf_float,
   parse_duration, dump_duration
 };
@@ -2319,6 +2353,25 @@ static int eq_penum(const union tvec_regval *rv0,
                    const struct tvec_regdef *rd)
   { return (rv0->p == rv1->p); }
 
+/* --- @copy_tenum@ --- *
+ *
+ * Arguments:  @union tvec_regval *rvd@ = destination register value
+ *             @const union tvec_regval *rvs@ = source register value
+ *             @const struct tvec_regdef *rd@ = register definition
+ *
+ * Returns:    ---
+ *
+ * Use:                Copy a register value.
+ */
+
+#define copy_ienum copy_int
+#define copy_uenum copy_uint
+#define copy_fenum copy_float
+
+static void copy_penum(union tvec_regval *rvd, const union tvec_regval *rvs,
+                      const struct tvec_regdef *rd)
+  { rvd->p = rvs->p; }
+
 /* --- @tobuf_tenum@ --- *
  *
  * Arguments:  @buf *b@ = buffer
@@ -2553,7 +2606,7 @@ TVEC_MISCSLOTS(DEFDUMP_ENUM)
 /* Enumeration type definitions. */
 #define DEFTY_ENUM(tag, ty, slot)                                      \
   const struct tvec_regty tvty_##slot##enum = {                                \
-    init_##slot##enum, trivial_release, eq_##slot##enum,               \
+    init_##slot##enum, trivial_release, eq_##slot##enum, copy_##slot##enum, \
     tobuf_##slot##enum, frombuf_##slot##enum,                          \
     parse_##slot##enum, dump_##slot##enum                              \
   };
@@ -2769,7 +2822,7 @@ static void dump_flags(const union tvec_regval *rv,
 
 /* Flags type definition. */
 const struct tvec_regty tvty_flags = {
-  init_uint, trivial_release, eq_uint,
+  init_uint, trivial_release, eq_uint, copy_uint,
   tobuf_uint, frombuf_uint,
   parse_flags, dump_flags
 };
@@ -3089,7 +3142,7 @@ static void dump_char(const union tvec_regval *rv,
 
 /* Character type definition. */
 const struct tvec_regty tvty_char = {
-  init_int, trivial_release, eq_int,
+  init_int, trivial_release, eq_int, copy_int,
   tobuf_char, frombuf_char,
   parse_char, dump_char
 };
@@ -3189,6 +3242,43 @@ static int eq_bytes(const union tvec_regval *rv0,
           MEMCMP(rv0->bytes.p, ==, rv1->bytes.p, rv1->bytes.sz)));
 }
 
+/* --- @copy_text@, @copy_bytes@ --- *
+ *
+ * Arguments:  @union tvec_regval *rvd@ = destination register value
+ *             @const union tvec_regval *rvs@ = source register value
+ *             @const struct tvec_regdef *rd@ = register definition
+ *
+ * Returns:    ---
+ *
+ * Use:                Copy a register value.
+ */
+
+static void copy_text(union tvec_regval *rvd, const union tvec_regval *rvs,
+                     const struct tvec_regdef *rd)
+{
+  size_t sz = rvs->text.sz;
+
+  if (!sz)
+    rvd->text.sz = 0;
+  else {
+    tvec_alloctext(rvd, sz);
+    memcpy(rvd->text.p, rvs->text.p, sz); rvd->text.p[sz] = 0;
+  }
+}
+
+static void copy_bytes(union tvec_regval *rvd, const union tvec_regval *rvs,
+                      const struct tvec_regdef *rd)
+{
+  size_t sz = rvs->bytes.sz;
+
+  if (!sz)
+    rvd->bytes.sz = 0;
+  else {
+    tvec_alloctext(rvd, sz);
+    memcpy(rvd->bytes.p, rvs->bytes.p, sz);
+  }
+}
+
 /* --- @tobuf_text@, @tobuf_bytes@ --- *
  *
  * Arguments:  @buf *b@ = buffer
@@ -3478,12 +3568,12 @@ static void dump_bytes(const union tvec_regval *rv,
 
 /* Text and byte string type definitions. */
 const struct tvec_regty tvty_text = {
-  init_text, release_text, eq_text,
+  init_text, release_text, eq_text, copy_text,
   tobuf_text, frombuf_text,
   parse_text, dump_text
 };
 const struct tvec_regty tvty_bytes = {
-  init_bytes, release_bytes, eq_bytes,
+  init_bytes, release_bytes, eq_bytes, copy_bytes,
   tobuf_bytes, frombuf_bytes,
   parse_bytes, dump_bytes
 };
@@ -3605,7 +3695,7 @@ void tvec_alloctext(union tvec_regval *rv, size_t sz)
 {
   if (rv->text.sz <= sz)
     { free(rv->text.p); rv->text.p = x_alloc(&arena_stdlib, sz + 1); }
-  memset(rv->text.p, '?', sz); rv->text.sz = sz;
+  memset(rv->text.p, '?', sz); rv->text.p[sz] = 0; rv->text.sz = sz;
 }
 
 void tvec_allocbytes(union tvec_regval *rv, size_t sz)
@@ -3672,6 +3762,27 @@ static int eq_buffer(const union tvec_regval *rv0,
          rv0->buf.m == rv1->buf.m);
 }
 
+/* --- @copy_buffer@ --- *
+ *
+ * Arguments:  @union tvec_regval *rvd@ = destination register value
+ *             @const union tvec_regval *rvs@ = source register value
+ *             @const struct tvec_regdef *rd@ = register definition
+ *
+ * Returns:    ---
+ *
+ * Use:                Copy a register value.
+ */
+
+static void copy_buffer(union tvec_regval *rvd, const union tvec_regval *rvs,
+                       const struct tvec_regdef *rd)
+{
+  if (rvd->buf.p) { free(rvd->buf.p); rvd->buf.p = 0; }
+  rvd->buf.sz = rvs->buf.sz;
+  rvd->buf.a = rvs->buf.a;
+  rvd->buf.m = rvs->buf.m;
+  rvd->buf.off = 0;
+}
+
 /* --- @tobuf_buffer@ --- *
  *
  * Arguments:  @buf *b@ = buffer
@@ -3831,7 +3942,7 @@ static void dump_buffer(const union tvec_regval *rv,
 
 /* Buffer type definition. */
 const struct tvec_regty tvty_buffer = {
-  init_buffer, release_buffer, eq_buffer,
+  init_buffer, release_buffer, eq_buffer, copy_buffer,
   tobuf_buffer, frombuf_buffer,
   parse_buffer, dump_buffer
 };
index 36f69bac21b175131235fd0b675b3cf2484a7d2a..e5cd05a26e8964299896ddfb32950b794e87c37d 100644 (file)
@@ -105,6 +105,7 @@ tvec \- test vector framework
 .B "};"
 .B "#define TVRF_UNSET ..."
 .B "#define TVRF_OPT ..."
+.B "#define TVRF_SYNTH ..."
 .B "#define TVRF_ID ..."
 .B "#define TVEC_ENDREGS ..."
 .PP
index eae88a7217a97e6e2cbc26dc6b82fd52884ab1ff..d5c1a3f8e5af31ddc07e786e52c67a20476a5809 100644 (file)
@@ -238,7 +238,8 @@ struct tvec_regdef {
   unsigned f;                          /* flags */
 #define TVRF_UNSET 1u                  /*   register may be marked unset */
 #define TVRF_OPT 2u                    /*   register need not be assigned */
-#define TVRF_ID 4u                     /*   part of test identity  */
+#define TVRF_SYNTH 4u                  /*   register must not be assigned */
+#define TVRF_ID 8u                     /*   part of test identity  */
   union tvec_misc arg;                 /* extra detail for the type */
 };
 #define TVEC_ENDREGS { 0, 0, 0, 0, { 0 } }
@@ -286,6 +287,11 @@ struct tvec_regty {
      * register as @rv0@ and the output as @rv1@.
      */
 
+  void (*copy)(union tvec_regval */*rvd*/,
+              const union tvec_regval */*rvs*/,
+              const struct tvec_regdef */*rd*/);
+    /* Copy the value from @rvs@ to @rvd@. */
+
   int (*tobuf)(buf */*b*/, const union tvec_regval */*rv*/,
               const struct tvec_regdef */*rd*/);
     /* Serialize the value @*rv@, writing the result to @b@.  Return zero on
@@ -895,31 +901,22 @@ extern PRINTF_LIKE(2, 3)
 extern PRINTF_LIKE(2, 3)
   void tvec_info(struct tvec_state */*tv*/, const char */*msg*/, ...);
 
-/* --- @tvec_unkregerr@ --- *
+/* --- @tvec_unkregerr@, @tvec_dupregerr@, @tvec_synthregerr@ --- *
  *
  * Arguments:  @struct tvec_state *tv@ = test-vector state
  *             @const char *name@ = register or pseudoregister name
  *
  * Returns:    %$-1$%.
  *
- * Use:                Reports an error that the register or pseudoregister is
- *             unrecognized.
+ * Use:                Reports an error about a misused register: @tvec_unkregerr@
+ *             reports that the register is unknown, @tvec_dupregerr@ that
+ *             it is already assigned, and @tvec_synthregerr@ that it is
+ *             synthetic.
  */
 
 extern int tvec_unkregerr(struct tvec_state */*tv*/, const char */*name*/);
-
-/* --- @tvec_dupregerr@ --- *
- *
- * Arguments:  @struct tvec_state *tv@ = test-vector state
- *             @const char *name@ = register or pseudoregister name
- *
- * Returns:    %$-1$%.
- *
- * Use:                Reports an error that the register or pseudoregister has been
- *             assigned already in the current test.
- */
-
 extern int tvec_dupregerr(struct tvec_state */*tv*/, const char */*name*/);
+extern int tvec_synthregerr(struct tvec_state */*tv*/, const char */*name*/);
 
 /*----- Built-in output drivers -------------------------------------------*/
 
diff --git a/ui/tty.c b/ui/tty.c
new file mode 100644 (file)
index 0000000..77e778a
--- /dev/null
+++ b/ui/tty.c
@@ -0,0 +1,514 @@
+/* -*-c-*-
+ *
+ * Terminal handling
+ *
+ * (c) 2024 Straylight/Edgeware
+ */
+
+/*----- Licensing notice --------------------------------------------------*
+ *
+ * This file is part of the mLib utilities library.
+ *
+ * mLib is free software: you can redistribute it and/or modify it under
+ * the terms of the GNU Library General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or (at
+ * your option) any later version.
+ *
+ * mLib is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU Library General Public
+ * License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with mLib.  If not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307,
+ * USA.
+ */
+
+/*----- Header files ------------------------------------------------------*/
+
+#include "config.h"
+
+#include <errno.h>
+#include <stdio.h>
+
+#include <unistd.h>
+#include <termios.h>
+
+#ifdef HAVE_TERMCAP
+#  include <termcap.h>
+#endif
+
+#ifdef HAVE_TERMINFO
+#  include <term.h>
+#endif
+
+#ifdef HAVE_UNIBILIUM
+#  include <unibilium.h>
+#endif
+
+#include "gprintf.h"
+#include "macros.h"
+#include "tty.h"
+
+/*----- Data structures ---------------------------------------------------*/
+
+/*----- Common support machinery ------------------------------------------*/
+
+static uint32 basic_colour_map[] = {
+  /* standard black    */ 0x000000,
+  /* standard red      */ 0xcc0000,
+  /* standard green    */ 0x00cc00,
+  /* standard yellow   */ 0xcccc00,
+  /* standard blue     */ 0x0000cc,
+  /* standard magenta  */ 0xcc00cc,
+  /* standard cyan     */ 0x00cccc,
+  /* standard white    */ 0xcccccc
+}, bright_colour_map[] = {
+  /* bright black      */ 0x333333,
+  /* bright red                */ 0xff3333,
+  /* bright green      */ 0x33ff33,
+  /* bright yellow     */ 0xffff33,
+  /* bright blue       */ 0x3333ff,
+  /* bright magenta    */ 0xff33ff,
+  /* bright cyan       */ 0x33ffff,
+  /* bright white      */ 0xffffff
+};
+
+static void clamp_colours(uint32 *space_out, uint32 *colour_out,
+                         uint32 space, uint32 colour, uint32 acaps)
+{
+  unsigned r, g, b, y, t, range;
+  uint32 best_colour, best_space; int best_error;
+
+#define COMMON_SCALE 3825
+
+#define CHECK_RANGE(r) do {                                            \
+  STATIC_ASSERT(COMMON_SCALE%(r) == 0, "common scale doesn't cover " #r); \
+} while (0)
+
+#define SET_RANGE(r) do { CHECK_RANGE(r); range = (r); } while (0)
+
+  /* Check the colour space.  If it's one that the terminal can handle, then
+   * return the colour unchanged.  Otherwise, extract the channel components
+   * for the next step.
+   */
+  switch (space) {
+    case TTCSPC_NONE:
+      *space_out = TTCSPC_NONE; *colour_out = 0; return;
+    case TTCSPC_1BPC:
+      if (colour >= 8) goto inval;
+      if (acaps&TTACF_1BPC)
+       { *space_out = TTACF_1BPC; *colour_out = colour; return; }
+      colour = basic_colour_map[colour];
+      r = TTCOL_8BR(colour); g = TTCOL_8BG(colour); b = TTCOL_8BB(colour);
+      SET_RANGE(255);
+      break;
+    case TTCSPC_1BPCBR:
+      if (colour >= 8) goto inval;
+      if (acaps&TTACF_1BPCBR)
+       { *space_out = TTACF_1BPCBR; *colour_out = colour; return; }
+      colour = bright_colour_map[colour];
+      r = TTCOL_8BR(colour); g = TTCOL_8BG(colour); b = TTCOL_8BB(colour);
+      SET_RANGE(255);
+      break;
+    case TTCSPC_2BPC:
+      if (colour >= 64) goto inval;
+      if (acaps&TTACF_2BPC)
+       { *space_out = TTACF_2BPC; *colour_out = colour; return; }
+      r = TTCOL_2BR(colour); g = TTCOL_2BG(colour); b = TTCOL_2BB(colour);
+      SET_RANGE(3);
+      break;
+    case TTCSPC_8LGS:
+      if (colour >= 8) goto inval;
+      if (acaps&TTACF_8LGS)
+       { *space_out = TTACF_8LGS; *colour_out = colour; return; }
+      r = g = b = colour + 1;
+      SET_RANGE(9);
+      break;
+    case TTCSPC_6LPC:
+      if (colour >= 216) goto inval;
+      if (acaps&TTACF_6LPC)
+       { *space_out = TTACF_6LPC; *colour_out = colour; return; }
+      r = TTCOL_6LR(colour); g = TTCOL_6LG(colour); b = TTCOL_6LB(colour);
+      SET_RANGE(5);
+      break;
+    case TTCSPC_24LGS:
+      if (colour >= 24) goto inval;
+      if (acaps&TTACF_24LGS)
+       { *space_out = TTACF_24LGS; *colour_out = colour; return; }
+      r = g = b = colour + 1;
+      SET_RANGE(25);
+      break;
+    case TTCSPC_8BPC:
+      if (colour >= 0x01000000) goto inval;
+      if (acaps&TTACF_8BPC)
+       { *space_out = TTACF_8BPC; *colour_out = colour; return; }
+      r = TTCOL_8BR(colour); g = TTCOL_8BG(colour); b = TTCOL_8BB(colour);
+      SET_RANGE(255);
+      break;
+      default:
+    goto inval;
+  }
+
+#undef SET_RANGE
+
+  /* We didn't get an exact match, so we'll have to make do with what we've
+   * got.
+   */
+  best_error = -1;
+
+#define RWT 2
+#define GWT 4
+#define BWT 1
+
+  t = COMMON_SCALE/range; r *= t; g *= t; b *= t;
+  y = (RWT*r + GWT*g + BWT*b + (RWT + GWT + BWT)/2)/(RWT + GWT + BWT);
+
+#define TRY_APPROX(red, grn, blu, spc, clr) do {                       \
+  int r_err = (red) - r, g_err = (grn) - g, b_err = (blu) - b;         \
+  int err;                                                             \
+                                                                       \
+  if (r_err < 0) r_err = -r_err;                                       \
+  if (g_err < 0) g_err = -g_err;                                       \
+  if (b_err < 0) b_err = -b_err;                                       \
+                                                                       \
+  err = r_err + g_err + b_err;                                         \
+  if (best_error < 0 || err < best_error)                              \
+    { best_error = err; best_space = (spc); best_colour = (clr); }     \
+} while (0)
+
+#define TRY_RGB(spc, lvls) do {                                                \
+  CHECK_RANGE((lvls) - 1);                                             \
+  unsigned sc = COMMON_SCALE/((lvls) - 1);                             \
+  unsigned rr, gg, bb, clr;                                            \
+                                                                       \
+  rr = (r + sc/2)/sc; gg = (g + sc/2)/sc; bb = (b + sc/2)/sc;          \
+  TRY_APPROX(rr*sc, gg*sc, bb*sc, (spc), (rr*(lvls) + bb)*(lvls) + gg); \
+} while (0)
+
+#define TRY_GREY(spc, lvls) do {                                       \
+  CHECK_RANGE((lvls) + 1);                                             \
+  unsigned sc = COMMON_SCALE/((lvls) + 1);                             \
+  unsigned yy, grey;                                                   \
+                                                                       \
+  yy = (y + sc/2)/sc;                                                  \
+  if (yy >= 1 && yy <= (lvls))                                         \
+    { grey = yy*sc; TRY_APPROX(grey, grey, grey, (spc), grey - 1); }   \
+} while (0)
+
+  if (acaps&TTACF_1BPC)
+    for (i = 0; i < 8; i++) {
+      t = basic_colour_map[i];
+      TRY_APPROX(TTCOL_8BR(t), TTCOL_8BG(t), TTCOL_8BB(t), TTCSPC_1BPC, i);
+    }
+  if (acaps&TTACF_1BPCBR)
+    for (i = 0; i < 8; i++) {
+      t = bright_colour_map[i];
+      TRY_APPROX(TTCOL_8BR(t), TTCOL_8BG(t), TTCOL_8BB(t), TTCSPC_1BPCBR, i);
+    }
+  if (acaps&TTACF_2BPC) TRY_RGB(TTCSPC_2BPC, 4);
+  if (acaps&TTACF_8LGS) TRY_GREY(TTCSPC_8LGS, 8);
+  if (acaps&TTACF_6LPC) TRY_RGB(TTCSPC_6LPC, 6);
+  if (acaps&TTACF_24LGS) TRY_GREY(TTCSPC_24LGS, 24);
+  if (acaps&TTACF_8BPC) TRY_RGB(TTCSPC_8BPC, 256);
+
+#undef TRY_APPROX
+#undef TRY_RGB
+#undef TRY_GREY
+
+#undef RWT
+#undef GWT
+#undef BWT
+
+  *space_out = best_space; *colour_out = best_colour;
+  return;
+
+inval:
+  *space_out = TTCSPC_NONE; *colour_out = 0;
+}
+
+void tty_clampattr(struct tty_attr *a_out, const struct tty_attr *a,
+                  uint32 acaps)
+{
+  uint32 ff = 0, f = a->f, t;
+
+  t = (f&TTAF_LNMASK) >> TTAF_LNSHIFT;
+  switch (t) {
+    case TTLN_NONE:
+      break;
+    case TTLN_ULINE:
+      if (!acaps&TTACF_ULINE) t = TTLN_NONE;
+      break;
+    case TTLN_UULINE:
+      if (acaps&TTACF_UULINE) ;
+      else if (acaps&TTACF_ULINE) t = TTLN_ULNE;
+      else t = TTLN_NONE;
+      break;
+    case TTLN_STRIKE:
+      if (!acaps&TTACF_STRIKE) t = TTLN_NONE;
+      break;
+    default:
+      t = TTLN_NONE;
+  }
+  ff |= t << TTAF_LNSHIFT;
+
+  t = (f&TTAF_WTMASK) >> TTAF_WTSHIFT;
+  switch (t) {
+    case TTWT_MED: break;
+    case TTWT_BOLD: if (!(acaps&TTACF_BOLD)) t = TTWT_MED; break;
+    case TTWT_DIM: if (!(acaps&TTACF_DIM)) t = TTWT_MED; break;
+    default: t = TTWD_MED; break;
+  }
+  ff |= t << TTAF_WTSHIFT;
+
+  if (acaps&TTACF_ITAL) ff |= f&TTAF_ITAL;
+  if (acaps&TTACF_INVV) ff |= f&TTAF_INVV;
+
+  if (acaps&TTACF_FG) {
+    clamp_colours(&t, &a_out->fg,
+                 (f&TTAF_FGSPCMASK) >> TTAF_FGSPCSHIFT, a->fg);
+    ff |= t << TTAF_FGSPCSHIFT;
+  }
+  if (acaps&TTACF_BG) {
+    clamp_colours(&t, &a_out->bg,
+                 (f&TTAF_BGSPCMASK) >> TTAF_BGSPCSHIFT, a->bg);
+    ff |= t << TTAF_BGSPCSHIFT;
+  }
+
+  a_out->f = ff; a_out->_res = 0;
+}
+
+/*----- Common machinery for `termcap' and `terminfo' ---------------------*/
+
+#if defined(HAVE_TERMINFO) ||                                          \
+    defined(HAVE_TERMCAP) ||                                           \
+    defined(HAVE_UNIBILIUM)
+
+#if defined(HAVE_TERMINFO) || defined(HAVE_TERMCAP)
+
+static const struct gprintf_ops *global_gops;
+static void *global_gout;
+
+static int term_putch(int ch)
+  { return (global_gops->putch(global_gout, ch)); }
+
+#endif
+
+#ifdef HAVE_UNIBILIUM
+#  define UNIBI_(x) unibi_##x
+#else
+#  define UNIBI_(x) 0
+#endif
+
+#define BASICCAPS(_bool, _int, _str)                                   \
+  _str(carriage_return, cr, cr)                                                \
+  _int(lines, lines, li) _int(columns, cols, co)
+
+#define ATTRCAPS(_bool, _int, _str)                                    \
+  _str(exit_attribute_mode, sgr0, me)                                  \
+  _str(enter_underline_mode, smul, us) _str(exit_underline_mode, rmul, ue) \
+  _str(enter_italics_mode, sitm, ZH) _str(exit_italics_mode, ritm, ZR) \
+  _str(enter_bold_mode, bold, md) _str(enter_dim_mode, dim, mh)                \
+  _str(enter_reverse_mode, rev, mr)                                    \
+  COLOURCAPS(_bool, _int, _str)
+
+#define COLOURCAPS(_bool, _int, _str)                                  \
+  _str(set_a_foreground, setaf, AF) _str(set_a_background, setab, AB)  \
+  _str(orig_pair, op, op) _int(max_colors, colors, Co)
+
+#define MODECAPS(_bool, _int, _str)                                    \
+  _str(enter_am_mode, smam, SA) _str(exit_am_mode, rmam, RA)           \
+  _str(enter_ca_mode, smcup, ti) _str(exit_ca_mode, rmcup, te)         \
+  _str(enter_insert_mode, smir, im) _str(exit_insert_mode, rmir, ei)
+
+#define MOVECAPS(_bool, _int, _str)                                    \
+  _str(carriage_return, cr, cr)                                                \
+  _str(cursor_home, home, ho)                                          \
+  _str(cusor_address, cup, cm)                                         \
+  _str(row_address, vpa, cv) _str(column_address, hpa, ch)             \
+  _str(cursor_left, cub1, le) _(parm_left_cursor, cub, LE)             \
+  _str(cursor_right, cuf1, nd) _(parm_right_cursor, cuf, RI)           \
+  _str(cursor_up, cuu1, up) _str(parm_up_cursor, cuu, UP)              \
+  _str(cursor_down, cud1, do) _str(parm_down_cursor, cud, DO)
+
+#define SCROLLCAPS(_bool, _int, _str)                                  \
+  _str(change_scroll_region, csr, cs)                                  \
+  _str(scroll_forward, ind, sf) _str(parm_index, indn, SF)             \
+  _str(scroll_reverse, ri, sr) _str(parm_rindex, rin, SR)
+
+#define ERASECAPS(_bool, _int, _str)                                   \
+  _str(clr_bol, el1, cb)                                               \
+  _str(clr_eol, el, ce)                                                        \
+  _str(clr_eos, ed, cd)
+
+#define INSDELCAPS(_bool, _int, _str)                                  \
+  _str(insert_character, ich1, ic) _str(parm_ich, ich, IC)             \
+  _str(insert_line, il1, al) _str(parm_insert_line, il, AL)            \
+  _str(delete_character, dch1, dc) _str(parm_dch, dch, DC)             \
+  _str(delete_line, dl1, dl) _str(parm_delete_line, dl, DL)
+
+#define STORECAPS(_bool, _int, _str)                                   \
+  ATTRCAPS(_bool, _int, _str)                                          \
+  MODECAPS(_bool, _int, _str)                                          \
+  MOVECAPS(_bool, _int, _str)                                          \
+  SCROLLCAPS(_bool, _int, _str)                                                \
+  ERASECAPS(_bool, _int, _str)                                         \
+  INSDELCAPS(_bool, _int, _str)
+
+#define CAP_XMC magic_cookie_glitch, xmc, sg
+#define CAP_BCE back_color_erase, bce, ut
+#define CAP_XHPA row_addr_glitch, xvpa, YD
+#define CAP_XVPA col_addr_glitch, xhpa, YA
+#define CAP_MIR move_insert_mode, mir, mi
+#define CAP_MSGR move_standout_mode, msgr, ms
+#define CAP_NPC no_pad_char, npc, NP
+#define CAP_AM auto_left_margin, am, am
+#define CAP_XENL eat_newline_glitch, xenl, xn
+
+#define CAPREF(var, info, cap) UNIBI_(var), #info, #cap
+
+struct ttycaps {
+#define DEF_STRCAP(info, cap) const char *cap;
+#define DEF_INTCAP(info, cap) int cap;
+  STORECAPS(DEF_STRCAP, DEF_INTCAP)
+#undef DEF_STRCAP
+#undef DEF_INTCAP
+};
+
+typedef int boolcapfn(int uix, const char *info, const char *cap, void *arg);
+typedef int intcapfn(int uix, const char *info, const char *cap, void *arg);
+typedef const char *strcapfn(int uix, const char *info, const char *cap,
+                            void *arg);
+
+#define DEFINE_CAPISH(PRE, pre)
+
+static int init_caps(struct tty *tty, struct tty_caps *caps,
+                    boolcapfn *boolcap, intcapfn *intcap, strcapfn *strcap,
+                    void *arg)
+{
+  tty->acaps = tty->ocaps = 0;
+  tty->st.modes = 0;
+  tty->st.attr.f = 0;
+
+  /* Inhale all of the interesting terminal capabilities. */
+#define GETBOOL(var, info, cap)                                                \
+       caps->info = boolcap(CAPREF(var, info, cap), arg);
+#define GETINT(var, info, cap)                                         \
+       caps->info = intcap(CAPREF(var, info, cap), arg);
+#define GETSTR(var, info, cap)                                         \
+       caps->info = strcap(CAPREF(var, info, cap), arg);
+  STORECAPS(GETBOOL, GETINT, GETSTR)
+#undef GETBOOL
+#undef GETINT
+#undef GETSTR
+
+#define CLEARCAP(var, info, cap) caps->info = 0;
+#define CLEAR_CAPS(caplist) do { caplist(CLEARCAP) } while (0)
+
+  /* Basic capabilities. */
+  if (!caps->cr) caps->cr = "\r";
+
+  /* Attribute capabilities. */
+  if (intcap(CAPREF(CAP_XMC)) || !caps->sgr0)
+    CLEAR_CAPS(ATTRCAPS);
+  else {
+    if (caps->smul) tty->acaps |= TTACF_ULINE;
+    if (caps->bold) tty->acaps |= TTACF_BOLD;
+    if (caps->dim) tty->acaps |= TTACF_DIM;
+    if (caps->sitm) tty->acaps |= TTACF_ITAL;
+    if (caps->rev) tty->acaps |= TTACF_INVV;
+
+    if (!caps->colors >= 8 || (!caps->setaf && !caps->setbf))
+      CLEAR_CAPS(COLOURCAPS);
+    else {
+      if (caps->setaf) tty->acaps |= TTACF_FG;
+      if (caps->setab) tty->acaps |= TTACF_BG;
+      tty->acaps |= TTACF_1BPC;
+      if (caps->colors >= 16) tty->acaps |= TTACF_1BPCBR;
+      if (caps->colors == 88) tty->acaps |= TTACF_2BPC | TTACF_8LGS;
+      else if (caps->colors >= 256) tty->acaps |= TTACF_6LPC | TTACF_24LGS;
+      if (caps->colors >= 16777216) tty->acaps |= TTACF_8BPC;
+      if (boolcap(CAPREF(CAP_BCE))) tty->ocaps |= TTCF_BGER;
+    }
+  }
+
+  /* Motion capabilities. */
+  if (boolcap(CAPREF(CAP_XVPA))) caps->vpa = 0;
+  if (boolcap(CAPREF(CAP_XHPA))) caps->hpa = 0;
+  if (!caps->cub1) caps->cub1 = "\b";
+  if (!caps->cud1) caps->cud1 = "\n";
+  if ((caps->cuf || caps->cuf1) && (caps->cuu || caps->cuu1)) {
+    tty->ocaps |= TTCF_RELMV;
+    if (caps->vpa) tty->ocaps |= TTCF_MIXMV;
+  }
+  if (caps->cup || (caps->hpa && caps->vpa)) tty->ocaps |= TTCF_ABSMV;
+
+  /* Mode capabilities. */
+  if (caps->smam && caps->rmam) tty->ocaps |= TTMF_AUTOM;
+  if (caps->smir && caps->rmir) tty->ocaps |= TTMF_INS;
+  if (boolcap(CAPREF(CAP_AM))) {
+    tty->st.modes |= TTMF_AUTOM;
+    if (boolcap(CAPREF(CAP_XENL))) tty->ocaps |= TTCF_MMARG;
+  }
+
+  /* Scrolling. */
+  if (caps->csr) tty->ocaps |= TTCF_SCRGN;
+  if ((caps->ind || caps->indn) && (caps->ri || caps->rin))
+    tty->ocaps |= TTCF_SCROLL;
+
+  /* Erasure. */
+  if (caps->ech) tty->ocaps |= TTCF_ERCH;
+  if (caps->el1) tty->ocaps |= TTCF_ERBOL;
+  if (caps->el) tty->ocaps |= TTCF_EREOL;
+  if (caps->ed) tty->ocaps |= TTCF_EREOD;
+
+  /* Insertion and deletion. */
+  if (caps->ich || caps->ich1) tty->ocaps |= TTCF_INSCH;
+  if (caps->il || caps->il1) tty->ocaps |= TTCF_INSLN;
+  if (caps->dch || caps->dch1) tty->ocaps |= TTCF_DELCH;
+  if (caps->dl || caps->dl1) tty->ocaps |= TTCF_DELLN;
+}
+
+static int caps_setattr(struct tty *tty, const struct tty_caps *caps,
+                       const struct gprintf_ops *gops, void *go,
+                       const struct tty_attr *a)
+{
+  struct tty_attr aa;
+
+  tty_clampattr(&aa, a, tty->acaps);
+  
+}
+
+static int caps_setmodes(struct tty *tty,
+                        const struct gprintf_ops *gops, void *go,
+                        uint32 modes_bic, uint32 modes_xor);
+static int caps_move(struct tty *tty,
+                    const struct gprintf_ops *gops, void *go,
+                    unsigned orig, int y, int x);
+static int caps_repeat(struct tty *tty,
+                      const struct gprintf_ops *gops, void *go,
+                      int ch, unsigned n);
+static int caps_erase(struct tty *tty,
+                     const struct gprintf_ops *gops, void *go,
+                     unsigned f);
+static int caps_erch(struct tty *tty,
+                    const struct gprintf_ops *gops, void *go,
+                    unsigned n);
+static int caps_ins(struct tty *tty,
+                   const struct gprintf_ops *gops, void *go,
+                   unsigned f, unsigned n);
+static int caps_del(struct tty *tty,
+                   const struct gprintf_ops *gops, void *go,
+                   unsigned f, unsigned n);
+static int caps_setscrgn(struct tty *tty,
+                        const struct gprintf_ops *gops, void *go,
+                        unsigned y0, unsigned y1);
+static int caps_scroll(struct tty *tty,
+                      const struct gprintf_ops *gops, void *go,
+                      int n);
+
+
+#endif
+
+/*----- That's all, folks -------------------------------------------------*/
diff --git a/ui/tty.h b/ui/tty.h
new file mode 100644 (file)
index 0000000..a82804d
--- /dev/null
+++ b/ui/tty.h
@@ -0,0 +1,277 @@
+/* -*-c-*-
+ *
+ * Terminal handling
+ *
+ * (c) 2024 Straylight/Edgeware
+ */
+
+/*----- Licensing notice --------------------------------------------------*
+ *
+ * This file is part of the mLib utilities library.
+ *
+ * mLib is free software: you can redistribute it and/or modify it under
+ * the terms of the GNU Library General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or (at
+ * your option) any later version.
+ *
+ * mLib is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU Library General Public
+ * License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with mLib.  If not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307,
+ * USA.
+ */
+
+#ifndef MLIB_TTY_H
+#define MLIB_TTY_H
+
+#ifdef __cplusplus
+  extern "C" {
+#endif
+
+/*----- Header files ------------------------------------------------------*/
+
+#include <stdio.h>
+
+#ifndef MLIB_BITS_H
+#  include "bits.h"
+#endif
+
+/*----- Data structures ---------------------------------------------------*/
+
+/* Attributes. */
+#define TTAF_LNMASK    0x0003u         /* line style mask */
+#define TTAF_LNSHIFT   0               /* line style shift */
+enum {
+  TTLN_NONE,                           /*   no line */
+  TTLN_ULINE,                          /*   underline */
+  TTLN_UULINE,                         /*   double underline */
+  TTLN_STRIKE,                         /*   strikeout */
+  TTLN_LIMIT
+};
+#define TTAF_WTMASK    0x000cu         /* weight mask */
+#define TTAG_WTSHIFT   2               /* weight shift */
+enum {
+  TTWT_MED,                            /*   medium */
+  TTWT_BOLD,                           /*   bold/bright */
+  TTWT_DIM,                            /*   light/dim */
+  TTWT_LIMIT
+};
+#define TTAF_ITAL      0x0010u         /* italic/oblique */
+#define TTAF_INVV      0x0020u         /* inverse video */
+#define TTAF_FGSPCMASK 0x1c00u         /* foreground space mask */
+#define TTAF_FGSPCSHIFT        10              /* foreground space shift */
+#define TTAF_BGSPCMASK 0xe000u         /* background space mask */
+#define TTAF_BGSPCSHIFT        13              /* background space shift */
+enum {
+  TTCSPC_NONE,                         /* no colour */
+  TTCSPC_1BPC,                         /* one bit per channel */
+  TTCSPC_1BPCBR,                       /* one bit per channel, brighter */
+  TTCSPC_2BPC,                         /* two bits levels per channel */
+  TTCSPC_8LGS,                         /* eight levels greyscale */
+  TTCSPC_6LPC,                         /* six levels per channel */
+  TTCSPC_24LGS,                                /* 24 levels greyscale */
+  TTCSPC_8BPC                          /* eight bits per channel */
+  TTCSPC_LIMIT
+};
+
+/* Colours. */
+#define TT1BC_RED      1u
+#define TT1BC_GREEN    2u
+#define TT1BC_BLUE     4u
+#define TT1BC_BRIGHT   8u
+#define TTCOL_BLACK    0u
+#define TTCOL_RED      (TT1BC_RED)
+#define TTCOL_GREEN    (TT1BC_GREEN)
+#define TTCOL_YELLOW   (TT1BC_RED | TT1BC_GREEN)
+#define TTCOL_BLUE     (TT1BC_BLUE)
+#define TTCOL_MAGENTA  (TT1BC_RED | TT1BC_BLUE)
+#define TTCOL_CYAN     (TT1BC_GREEN | TT1BC_BLUE)
+#define TTCOL_WHITE    (TT1BC_RED | TT1BC_GREEN | TT1BC_BLUE)
+#define TTCOL_BRBLACK  (TTCOL_BLACK | TT1BC_BRIGHT)
+#define TTCOL_BRRED    (TTCOL_RED | TT1BC_BRIGHT)
+#define TTCOL_BRGREEN  (TTCOL_GREEN | TT1BC_BRIGHT)
+#define TTCOL_BRYELLOW (TTCOL_YELLOW | TT1BC_BRIGHT)
+#define TTCOL_BRBLUE   (TTCOL_BLUE | TT1BC_BRIGHT)
+#define TTCOL_BRMAGENTA        (TTCOL_MAGENTA | TT1BC_BRIGHT)
+#define TTCOL_BRCYAN   (TTCOL_CYAN | TT1BC_BRIGHT)
+#define TTCOL_BRWHITE  (TTCOL_WHITE | TT1BC_BRIGHT)
+
+#define TTCOL_MK2B(r, g, b) (((r) << 4) | ((g) <<  2) | ((b) <<  0))
+#define TTCOL_2BR(col) (((col) >> 4)&0x03)
+#define TTCOL_2BG(col) (((col) >> 2)&0x03)
+#define TTCOL_2BB(col) (((col) >> 0)&0x03)
+
+#define TTCOL_MK6L(r, g, b) (36*(r) + 6*(g) + (b))
+#define TTCOL_6LR(col) ((col)/36)
+#define TTCOL_6LG(col) (((col)/6)%6)
+#define TTCOL_6LB(col) ((col)%6)
+
+#define TTCOL_MK8B(r, g, b) (((r) << 16) | ((g) <<  8) | ((b) <<  0))
+#define TTCOL_8BR(col) (((col) >> 16)&0xff)
+#define TTCOL_8BG(col) (((col) >>  8)&0xff)
+#define TTCOL_8BB(col) (((col) >>  0)&0xff)
+
+struct tty_attr {
+  uint32 f, _res0;                     /* attribute flags, reserved */
+  uint32 fg, bg;                       /* foreground/background colours */
+};
+
+/* Mode settings. */
+#define TTMF_AUTOM     0x00000001u     /* automatic margins */
+#define TTMF_FSCRM     0x00000002u     /* full-screen mode */
+#define TTMF_INS       0x00000004u     /* insert mode */
+
+/* Attribute capabilities. */
+#define TTACF_ULINE    0x00000001u     /* underline */
+#define TTACF_UULINE   0x00000002u     /* double underline */
+#define TTACF_STRIKE   0x00000004u     /* strikeout */
+#define TTACF_BOLD     0x00000008u     /* bold/bright */
+#define TTACF_DIM      0x00000010u     /* light/dim */
+#define TTACF_ITAL     0x00000020u     /* italic/oblique */
+#define TTACF_INVV     0x00000040u     /* inverse video */
+#define TTACF_FG       0x00000080u     /* set foreground colour */
+#define TTACF_BG       0x00000100u     /* set background colour */
+#define TTACF_1BPC     0x00000200u     /* one-bit-per-channel space */
+#define TTACF_1BPCBR   0x00000400u    /* one-bit-per-channel bright space */
+#define TTACF_2BPC     0x00000800u     /* two-bits-per-channel space */
+#define TTACF_8LGS     0x00001000u     /* 8-levels-greyscale space */
+#define TTACF_6LPC     0x00002000u     /* six-levels-per-channel space */
+#define TTACF_24LGS    0x00004000u     /* 24-levels-greyscale space */
+#define TTACF_8BPC     0x00008000u     /* eight-bits-per-channel space */
+
+/* Other capabilities. */
+#define TTCF_RELMV     0x00000100u     /* relative cursor motion */
+#define TTCF_ABSMV     0x00000200u     /* absolute cursor motion */
+#define TTCF_MIXMV     0x00000400u     /* mixed cursor motion */
+#define TTCF_MVINS     0x00000800u     /* motion preserves insert mode */
+#define TTCF_MVATTR    0x00001000u     /* motion preserves attributes */
+#define TTCF_MMARG     0x00002000u     /* proper magic margins */
+#define TTCF_SCRGN     0x00004000u     /* scroll regions */
+#define TTCF_SCROLL    0x00008000u     /* explicit scrolling */
+#define TTCF_BGER      0x00010000u     /* erasure uses background colour */
+#define TTCF_ERCH      0x00020000u     /* erase characters */
+#define TTCF_ERBOL     0x00040000u     /* erase from beginning of line */
+#define TTCF_EREOL     0x00080000u     /* erase to end of line */
+#define TTCF_ERBOD     0x00100000u     /* erase from beginning of display */
+#define TTCF_EREOD     0x00200000u     /* erase to end of display */
+#define TTCF_ERDSP     0x00400000u     /* erase display */
+#define TTCF_INSCH     0x00800000u     /* insert character */
+#define TTCF_INSLN     0x01000000u     /* insert line */
+#define TTCF_DELCH     0x02000000u     /* delete character */
+#define TTCF_DELLN     0x04000000u     /* delete line */
+
+struct tty_attrlist {
+  uint32 cap_mask, cap_eq;             /* capabilities to select */
+  struct tty_attr attr;                        /* attributes to set */
+};
+#define TTY_ATTRLIST_END { 0, 0, { 0, 0, 0, 0 } }
+
+/* Terminal capability backend libraries. */
+enum {
+  TTLIB_TERMCAP,                       /* traditional `termcap' */
+  TTLIB_TERMINFO,                      /* standard `terminfo' */
+  TTLIB_UNIBILIUM,                     /* `Unibiliium' */
+  TTLIB_XTERM,                      /* assume modern terminal conventions */
+  TTLIB_LIMIT
+};
+
+struct tty_state {
+  uint32 modes;
+  struct tty_attr attr;
+};
+
+struct tty {
+  const struct tty_ops *ops;
+  FILE *fp;
+  unsigned baud, ht, wd;
+  uint32 acaps, ocaps;
+  struct tty_state st;
+};
+
+/* Motion origin. */
+#define TTOF_XHOME 0u                  /* absolute horizontal motion */
+#define TTOF_XCUR 1u                   /* relative horizontal motion */
+#define TTOF_YHOME 0u                  /* absolute vertical motion */
+#define TTOF_YCUR 2u                   /* relative vertical motion */
+
+/* Erasure scope. */
+#define TTEF_LINE 0u                   /* erase within line */
+#define TTEF_DSP 1u                    /* erase whole display */
+#define TTEF_BEGIN 2u                  /* erase from beginning */
+#define TTEF_END 4u                    /* erase to end */
+
+/* Insertion and deletion. */
+#define TTIDF_CH 0u                    /* insert/delete characters */
+#define TTIDF_LN 1u                    /* insert/delete lines */
+
+struct tty_ops {
+  int (*setattr)(struct tty */*tty*/,
+                const struct gprintf_ops */*gops*/, void */*go*/,
+                const struct tty_attr */*old*/,
+                const struct tty_attr */*new*/);
+  int (*setmodes)(struct tty */*tty*/,
+                 const struct gprintf_ops */*gops*/, void */*go*/,
+                 uint32 /*modes_bic*/, uint32 /*modes_xor*/);
+  int (*move)(struct tty */*tty*/,
+             const struct gprintf_ops */*gops*/, void */*go*/,
+             unsigned /*orig*/, int /*y*/, int /*x*/);
+  int (*repeat)(struct tty */*tty*/,
+               const struct gprintf_ops */*gops*/, void */*go*/,
+               int /*ch*/, unsigned /*n*/);
+  int (*erase)(struct tty */*tty*/,
+              const struct gprintf_ops */*gops*/, void */*go*/,
+              unsigned /*f*/);
+  int (*erch)(struct tty */*tty*/,
+             const struct gprintf_ops */*gops*/, void */*go*/,
+             unsigned /*n*/);
+  int (*ins)(struct tty */*tty*/,
+            const struct gprintf_ops */*gops*/, void */*go*/,
+            unsigned /*f*/, unsigned /*n*/);
+  int (*del)(struct tty */*tty*/,
+            const struct gprintf_ops */*gops*/, void */*go*/,
+            unsigned /*f*/, unsigned /*n*/);
+  int (*setscrgn)(struct tty */*tty*/,
+                 const struct gprintf_ops */*gops*/, void */*go*/,
+                 unsigned /*y0*/, unsigned /*y1*/);
+  int (*scroll)(struct tty */*tty*/,
+               const struct gprintf_ops */*gops*/, void */*go*/,
+               int /*n*/);
+};
+
+/*----- Functions provided ------------------------------------------------*/
+
+/* caps
+ *
+ * RA  auto-wrap off
+ * SA  auto-wrap on
+
+ * AB  ansi background
+ * AF  ansi foreground
+ * md  bold
+ * me  clear text highlighting
+ * mr  inverse video
+ * op  default colour pair
+
+ * up  cursor up 1 line
+
+ * cd  clear to end-of-display
+ * ce  clear to end-of-line
+
+ * cr  carriage return
+ * nw  newline
+
+ * pc  pad character
+ */
+
+
+/*----- That's all, folks -------------------------------------------------*/
+
+#ifdef __cplusplus
+  }
+#endif
+
+#endif
index 83f3d8705692243b64a07f45e80d510443bf73f8..4fb8472cbc54ef9f2c8ec3a8e61e3a39425265b1 100644 (file)
@@ -169,7 +169,7 @@ struct ttycolour_state {
  *             named by @user@; otherwise, the user string is @user@ itself.
  *             The user string consists of a sequence of capability
  *             defintiions separated by colons.  Each definition has the
- *             form %%\syntax{<tok>"="<codes>}%%, 
+ *             form %%\syntax{<tok>"="<codes>}%%,                      
  */
 
 #define TCIF_GETENV 1u
index 5b772252cc940a33be8b2b0e8c2baabdbfac63c6..2ca5a06c2d316deb25d98f4baa865f2dbe98f10e 100644 (file)
 #  define LAUNDER(x)                                                   \
        ({ __typeof__(x) _y; __asm__("" : "=g"(_y) : "0"(x)); _y; })
 #  define ADMIRE(x)                                                    \
-       ({ __asm__("" : : "g"(x)); })
+       ({ __asm__("" :: "g"(x)); })
 #  define ADMIRE_BUF(p, sz)                                            \
-       ({ __asm__("" : : "g"(p), "g"(sz)); })
-#  define RELAX do __asm__ __volatile__("" ::: "memory"); while (0)
+       ({ __asm__("" :: "m"(*(unsigned char *)p), "g"(sz) : "memory"); })
+#  define RELAX do __asm__(""); while (0)
 #endif
 
 #if CLANG_VERSION_P(3, 3)