In GNU, Heirloom, and Plan 9 roff, tab positions apply to *input* lines,
authorschwarze <schwarze@openbsd.org>
Mon, 15 Aug 2022 13:01:40 +0000 (13:01 +0000)
committerschwarze <schwarze@openbsd.org>
Mon, 15 Aug 2022 13:01:40 +0000 (13:01 +0000)
not to *output* lines.  In particular, if an input line gets broken in
fill mode and a tab occurs in the second output line, it advances to a
position of at least (width of the first output line) + (width of a
space character even though this is never printed) + (width of the part
of the second output line that precedes the tab).

Implement the same logic in mandoc.

Again, do not use tabs in filled text: they have surprising effects,
including this one.

usr.bin/mandoc/man_term.c
usr.bin/mandoc/term.c
usr.bin/mandoc/term.h

index 2152cde..2433abc 100644 (file)
@@ -1,4 +1,4 @@
-/* $OpenBSD: man_term.c,v 1.190 2022/04/27 17:04:15 schwarze Exp $ */
+/* $OpenBSD: man_term.c,v 1.191 2022/08/15 13:01:40 schwarze Exp $ */
 /*
  * Copyright (c) 2010-2015,2017-2020,2022 Ingo Schwarze <schwarze@openbsd.org>
  * Copyright (c) 2008-2012 Kristaps Dzonsons <kristaps@bsd.lv>
@@ -973,6 +973,7 @@ out:
            ! (p->flags & (TERMP_NOBREAK | TERMP_NONEWLINE)) &&
            (n->next == NULL || n->next->flags & NODE_LINE)) {
                p->flags |= TERMP_BRNEVER | TERMP_NOSPACE;
+               p->tcol->taboff = 0;
                if (n->string != NULL && *n->string != '\0')
                        term_flushln(p);
                else
index b57296c..fe098f7 100644 (file)
@@ -1,4 +1,4 @@
-/* $OpenBSD: term.c,v 1.147 2022/08/15 10:21:01 schwarze Exp $ */
+/* $OpenBSD: term.c,v 1.148 2022/08/15 13:01:40 schwarze Exp $ */
 /*
  * Copyright (c) 2010-2022 Ingo Schwarze <schwarze@openbsd.org>
  * Copyright (c) 2008, 2009, 2010, 2011 Kristaps Dzonsons <kristaps@bsd.lv>
@@ -155,6 +155,7 @@ term_flushln(struct termp *p)
                /* Finally, print the field content. */
 
                term_field(p, vbl, nbr);
+               p->tcol->taboff += vbr + (*p->width)(p, ' ');
 
                /*
                 * If there is no text left in the field, exit the loop.
@@ -307,7 +308,9 @@ term_fill(struct termp *p, size_t *nbr, size_t *vbr, size_t vtarget)
                default:
                        switch (p->tcol->buf[ic]) {
                        case '\t':
+                               vis += p->tcol->taboff;
                                vis = term_tab_next(vis);
+                               vis -= p->tcol->taboff;
                                break;
                        case ASCII_NBRSP:  /* Non-breakable space. */
                                p->tcol->buf[ic] = ' ';
@@ -346,8 +349,8 @@ term_field(struct termp *p, size_t vbl, size_t nbr)
 {
        size_t   ic;    /* Character position in the input buffer. */
        size_t   vis;   /* Visual position of the current character. */
+       size_t   vt;    /* Visual position including tab offset. */
        size_t   dv;    /* Visual width of the current character. */
-       size_t   vn;    /* Visual position of the next character. */
 
        vis = 0;
        for (ic = p->tcol->col; ic < nbr; ic++) {
@@ -362,13 +365,13 @@ term_field(struct termp *p, size_t vbl, size_t nbr)
                case ASCII_BREAK:
                        continue;
                case '\t':
-                       vn = term_tab_next(vis);
-                       vbl += vn - vis;
-                       vis = vn;
-                       continue;
                case ' ':
                case ASCII_NBRSP:
-                       dv = (*p->width)(p, ' ');
+                       if (p->tcol->buf[ic] == '\t') {
+                               vt = p->tcol->taboff + vis;
+                               dv = term_tab_next(vt) - vt;
+                       } else
+                               dv = (*p->width)(p, ' ');
                        vbl += dv;
                        vis += dv;
                        continue;
@@ -430,7 +433,7 @@ endline(struct termp *p)
 void
 term_newln(struct termp *p)
 {
-
+       p->tcol->taboff = 0;
        p->flags |= TERMP_NOSPACE;
        if (p->tcol->lastcol || p->viscol)
                term_flushln(p);
index deba935..afdc559 100644 (file)
@@ -1,4 +1,4 @@
-/* $OpenBSD: term.h,v 1.76 2021/10/04 18:56:24 schwarze Exp $ */
+/* $OpenBSD: term.h,v 1.77 2022/08/15 13:01:40 schwarze Exp $ */
 /*
  * Copyright (c) 2008, 2009, 2010, 2011 Kristaps Dzonsons <kristaps@bsd.lv>
  * Copyright (c) 2011-2015, 2017, 2019 Ingo Schwarze <schwarze@openbsd.org>
@@ -56,6 +56,7 @@ struct        termp_col {
        size_t            col;          /* Byte in buf to be written. */
        size_t            rmargin;      /* Current right margin. */
        size_t            offset;       /* Current left margin. */
+       size_t            taboff;       /* Offset for literal tabs. */
 };
 
 struct termp {