At 2024-02-20T13:56:08+0000, Lennart Jablonka wrote:
> Quoth G. Branden Robinson:
> > > You know, just in case you want to take another look at that patch I
> > > sent a while ago.  For grotty.  To use terminfo.
> > 
> > […]
> > 
> > So I will take another look, yes.
> 
> I guess then I’ll re-start pinging you every other week or so.

That's fine.  I've started decomposing the patch into smaller units and
slightly revising it.  If Savannah supported force-pushes to branches
and per-branch disablement of announcements to a mailing list, I'd do
this work in a more visible place.

> Or were you expecting me to re-send the patch?

Not at all!

I'm attaching my progress so far.

Regards,
Branden
commit 66c80ccd610e7d1da50ddbc2ec61c7e356b6afc8
Author:     G. Branden Robinson <g.branden.robin...@gmail.com>
AuthorDate: Thu Feb 8 21:37:31 2024 -0600
Commit:     G. Branden Robinson <g.branden.robin...@gmail.com>
CommitDate: Wed Feb 21 11:56:15 2024 -0600

    [grotty]: Add terminfo support (3/x).

diff --git a/configure.ac b/configure.ac
index 2493fcebf..ff1730253 100644
--- a/configure.ac
+++ b/configure.ac
@@ -158,6 +158,9 @@ GROFF_URW_FONTS_PATH
 # use groff's own malloc-based allocator for C++ new/delete operators
 GROFF_USE_GROFF_ALLOCATOR
 
+# grotty uses curses
+GROFF_CURSES
+
 # other random stuff
 GROFF_BROKEN_SPOOLER_FLAGS
 GROFF_PAGE
diff --git a/m4/groff.m4 b/m4/groff.m4
index ebbe60d52..7fb58266a 100644
--- a/m4/groff.m4
+++ b/m4/groff.m4
@@ -1934,3 +1934,13 @@
     [test "$enableval" = yes && groff_use_own_allocator=yes],
     [groff_use_own_allocator=no])
 ])
+
+
+# Search for curses/terminfo library used by grotty.
+#
+# TODO: This needs be to generalized to check for other curses
+# implementations, not just ncurses.
+
+AC_DEFUN([GROFF_CURSES], [
+  PKG_CHECK_MODULES([CURSES], [ncurses])
+])
diff --git a/src/devices/grotty/grotty.am b/src/devices/grotty/grotty.am
index a9d2fe1c1..9e0540686 100644
--- a/src/devices/grotty/grotty.am
+++ b/src/devices/grotty/grotty.am
@@ -21,7 +21,7 @@ grotty_LDADD = $(LIBM) \
   libdriver.a \
   libgroff.a \
   lib/libgnu.a \
-  -lcurses
+  $(CURSES_LIBS)
 man1_MANS += src/devices/grotty/grotty.1
 EXTRA_DIST += \
   src/devices/grotty/grotty.1.man \

commit 6f002fe31706dc911d4036d73ae12f04654f8325
Author:     G. Branden Robinson <g.branden.robin...@gmail.com>
AuthorDate: Thu Feb 8 01:08:40 2024 -0600
Commit:     G. Branden Robinson <g.branden.robin...@gmail.com>
CommitDate: Wed Feb 21 11:56:15 2024 -0600

    [grotty]: Add terminfo support (2/x).

diff --git a/src/devices/grotty/grotty.am b/src/devices/grotty/grotty.am
index 14921c562..a9d2fe1c1 100644
--- a/src/devices/grotty/grotty.am
+++ b/src/devices/grotty/grotty.am
@@ -20,7 +20,8 @@ grotty_SOURCES = src/devices/grotty/tty.cpp
 grotty_LDADD = $(LIBM) \
   libdriver.a \
   libgroff.a \
-  lib/libgnu.a
+  lib/libgnu.a \
+  -lcurses
 man1_MANS += src/devices/grotty/grotty.1
 EXTRA_DIST += \
   src/devices/grotty/grotty.1.man \
diff --git a/src/devices/grotty/tty.cpp b/src/devices/grotty/tty.cpp
index 9501c656a..22db1a933 100644
--- a/src/devices/grotty/tty.cpp
+++ b/src/devices/grotty/tty.cpp
@@ -21,6 +21,9 @@ along with this program.  If not, see <http://www.gnu.org/licenses/>. */
 #include "device.h"
 #include "ptable.h"
 
+#include <curses.h>
+#include <term.h>
+
 typedef signed char schar;
 
 declare_ptable(schar)
@@ -56,8 +59,9 @@ static bool do_sgr_italics;
 static bool want_reverse_video_for_italics = false;
 static bool do_reverse_video;
 static bool use_overstriking_drawing_scheme = false;
+static bool want_capability_warnings = false;
 
-static void update_options();
+static void initialize_terminal();
 static void usage(FILE *stream);
 
 static int hline_char = '-';
@@ -916,8 +920,86 @@ printer *make_printer()
   return new tty_printer();
 }
 
-static void update_options()
+static void initialize_terminal()
 {
+  int err;
+
+  char *terminal_type = strdup(getenv("TERM"));
+  int ret = setupterm(NULL /* use TERM */, STDOUT_FILENO, &err);
+  size_t len = strlen(terminal_type);
+
+  if (len == 0)
+      fatal("TERM environment variable must be set; aborting");
+
+  if (OK == ret) {
+    ;
+  }
+  else if (ERR == ret) {
+    const char msg1[] = "entry for '";
+    const char msg2[] = "' not found in terminal database";
+    const int bufsz = len + sizeof msg1 + sizeof msg2 + 1 /* `\0` */;
+    char *errbuf = static_cast<char *>(calloc(bufsz, sizeof (char)));
+    (void) snprintf(errbuf, bufsz, "%s%s%s", msg1, terminal_type, msg2);
+    const char no_database[] = "terminfo database entry not found";
+
+    switch (err) {
+    case -1:
+      fatal(no_database);
+      break;
+    case 0:
+      fatal(errbuf);
+    case 1:
+      // POSIX calls this case "Success".  As an extension, to ncurses
+      // it means a hardcopy terminal.  While fatal for many terminfo
+      // applications, we're fine with it.
+      break;
+    default:
+      fatal("terminfo interface is nonstandard; aborting");
+    }
+  }
+  else
+    fatal("terminfo interface is nonstandard; aborting");
+
+  if (tigetflag("hc")) {
+    // hardcopy terminal
+    do_sgr_italics = false;
+    do_reverse_video = false;
+
+    if (want_sgr_italics) {
+      if (want_capability_warnings)
+	warning("terminal type %1 is incapable of SGR italics",
+		terminal_type);
+      want_sgr_italics = false;
+    }
+  }
+  else
+    if (want_sgr_italics)
+      if ((tigetstr("sitm") == 0 /* nullptr */)
+	   || (tigetstr("ritm") == 0 /* nullptr */)) {
+	if (want_capability_warnings)
+	  warning("terminal type %1 is incapable of SGR italics",
+		  terminal_type);
+	want_sgr_italics = false;
+      }
+
+  // X/Open Curses Issue 7 defines "os" as "Terminal overstrikes on
+  // hard-copy terminal".  However, ncurses's terminfo database defines
+  // it for graphical terminals as well; see entries for wy99gt-tek,
+  // wy370-tek, tek, tek4013, gt40, and so on.  We therefore regard it
+  // as generally relevant.
+  if (tigetflag("os")) {
+    // can overstrike
+    do_bold = want_emboldening_by_overstriking;
+    do_underline = want_italics_by_underlining;
+  }
+  else
+    if (want_emboldening_by_overstriking
+	|| want_italics_by_underlining
+	|| want_glyph_composition_by_overstriking)
+      if (want_capability_warnings)
+	warning("terminal type %1 is incapable of overstriking",
+		terminal_type);
+
   if (use_overstriking_drawing_scheme) {
     do_sgr_italics = false;
     do_reverse_video = false;
@@ -932,6 +1014,8 @@ static void update_options()
     do_bold = true;
     do_underline = true;
   }
+
+  free(terminal_type);
 }
 
 int main(int argc, char **argv)
@@ -948,8 +1032,8 @@ int main(int argc, char **argv)
     { "version", no_argument, 0, 'v' },
     { NULL, 0, 0, 0 }
   };
-  while ((c = getopt_long(argc, argv, "bBcdfF:hiI:oruUv", long_options, NULL))
-	 != EOF)
+  while ((c = getopt_long(argc, argv, "bBcdfF:hiI:oruUvw", long_options,
+	  NULL)) != EOF)
     switch(c) {
     case 'v':
       printf("GNU grotty (groff) version %s\n", Version_string);
@@ -1004,6 +1088,9 @@ int main(int argc, char **argv)
       // Ignore \D commands.
       allow_drawing_commands = false;
       break;
+    case 'w':
+      want_capability_warnings = true;
+      break;
     case CHAR_MAX + 1: // --help
       usage(stdout);
       break;
@@ -1014,7 +1101,7 @@ int main(int argc, char **argv)
     default:
       assert(0 == "unhandled getopt_long return value");
     }
-  update_options();
+  initialize_terminal();
   if (optind >= argc)
     do_file("-");
   else {

commit b28f4ba70ae8610b34a12f5c249b2601b12a6a3e
Author:     G. Branden Robinson <g.branden.robin...@gmail.com>
AuthorDate: Thu Feb 8 01:04:28 2024 -0600
Commit:     G. Branden Robinson <g.branden.robin...@gmail.com>
CommitDate: Wed Feb 21 11:56:15 2024 -0600

    [grotty]: Add terminfo support (1/x).
    
    C++ name spacing does not help us here because ncurses defines `lines`
    as a preprocessor macro.  (Curses exposes _every_ terminal capability as
    a global symbol; see Curses Issue 7, §7.1.3, "Defined Capabilities", p.
    340 or term_variables(3ncurses).)

diff --git a/src/devices/grotty/tty.cpp b/src/devices/grotty/tty.cpp
index c9f386e37..9501c656a 100644
--- a/src/devices/grotty/tty.cpp
+++ b/src/devices/grotty/tty.cpp
@@ -172,7 +172,7 @@ public:
 
 
 class tty_printer : public printer {
-  tty_glyph **lines;
+  tty_glyph **grotty_lines;
   int nlines;
   int cached_v;
   int cached_vpos;
@@ -271,16 +271,16 @@ tty_printer::tty_printer() : cached_v(0)
   (void)tty_color(color::MAX_COLOR_VAL, 0, color::MAX_COLOR_VAL, &dummy, 5);
   (void)tty_color(0, color::MAX_COLOR_VAL, color::MAX_COLOR_VAL, &dummy, 6);
   nlines = 66;
-  lines = new tty_glyph *[nlines];
+  grotty_lines = new tty_glyph *[nlines];
   for (int i = 0; i < nlines; i++)
-    lines[i] = 0;
+    grotty_lines[i] = 0;
   is_continuously_underlining = false;
 }
 
 tty_printer::~tty_printer()
 {
   current_lineno = 0; // At this point, we've read all the input.
-  delete[] lines;
+  delete[] grotty_lines;
 }
 
 void tty_printer::make_underline(int w)
@@ -373,11 +373,11 @@ void tty_printer::add_char(output_character c, int w,
 	    " quantum");
     vpos = v / font::vert;
     if (vpos > nlines) {
-      tty_glyph **old_lines = lines;
-      lines = new tty_glyph *[vpos + 1];
-      memcpy(lines, old_lines, nlines * sizeof(tty_glyph *));
+      tty_glyph **old_lines = grotty_lines;
+      grotty_lines = new tty_glyph *[vpos + 1];
+      memcpy(grotty_lines, old_lines, nlines * sizeof(tty_glyph *));
       for (int i = nlines; i <= vpos; i++)
-	lines[i] = 0;
+	grotty_lines[i] = 0;
       delete[] old_lines;
       nlines = vpos + 1;
     }
@@ -404,7 +404,7 @@ void tty_printer::add_char(output_character c, int w,
   // at each hpos, and otherwise in order of occurrence.
 
   tty_glyph **pp;
-  for (pp = lines + (vpos - 1); *pp; pp = &(*pp)->next)
+  for (pp = grotty_lines + (vpos - 1); *pp; pp = &(*pp)->next)
     if ((*pp)->hpos < hpos
 	|| ((*pp)->hpos == hpos && (*pp)->order() >= g->order()))
       break;
@@ -743,24 +743,24 @@ void tty_printer::end_page(int page_length)
   int lines_per_page = page_length / font::vert;
   int last_line;
   for (last_line = nlines; last_line > 0; last_line--)
-    if (lines[last_line - 1])
+    if (grotty_lines[last_line - 1])
       break;
 #if 0
   if (last_line > lines_per_page) {
     error("characters past last line discarded");
     do {
       --last_line;
-      while (lines[last_line]) {
-	tty_glyph *tem = lines[last_line];
-	lines[last_line] = tem->next;
+      while (grotty_lines[last_line]) {
+	tty_glyph *tem = grotty_lines[last_line];
+	grotty_lines[last_line] = tem->next;
 	delete tem;
       }
     } while (last_line > lines_per_page);
   }
 #endif
   for (int i = 0; i < last_line; i++) {
-    tty_glyph *p = lines[i];
-    lines[i] = 0;
+    tty_glyph *p = grotty_lines[i];
+    grotty_lines[i] = 0;
     tty_glyph *g = 0;
     while (p) {
       tty_glyph *tem = p->next;

Attachment: signature.asc
Description: PGP signature

Reply via email to