Implement automatic line breaking
authorschwarze <schwarze@openbsd.org>
Mon, 12 Jun 2017 18:55:42 +0000 (18:55 +0000)
committerschwarze <schwarze@openbsd.org>
Mon, 12 Jun 2017 18:55:42 +0000 (18:55 +0000)
inside individual table cells that contain text blocks.
This cures overlong lines in various Xenocara manuals.

regress/usr.bin/mandoc/char/unicode/input.out_ascii
usr.bin/mandoc/mandoc_aux.c
usr.bin/mandoc/mandoc_aux.h
usr.bin/mandoc/tbl_term.c
usr.bin/mandoc/term.c
usr.bin/mandoc/term.h

index 7711574..410bdc8 100644 (file)
@@ -22,7 +22,7 @@ D\bDE\bES\bSC\bCR\bRI\bIP\bPT\bTI\bIO\bON\bN
        U+007f   0xc1bf     ??         highest obfuscated ASCII
                 0xc278     ?x         ASCII continuation
        U+0080   0xc280     <80><80>   lowest two-byte
-                0xc2c380   ?`\bA       high continuation
+                0xc2c380   ?`\bA         high continuation
        U+07FF   0xdfbf     <?><?>     highest two-byte
 
    T\bTh\bhr\bre\bee\be-\b-b\bby\byt\bte\be r\bra\ban\bng\bge\be
index 0536595..6fa396a 100644 (file)
@@ -1,7 +1,7 @@
-/*     $OpenBSD: mandoc_aux.c,v 1.7 2015/10/12 21:09:08 schwarze Exp $ */
+/*     $OpenBSD: mandoc_aux.c,v 1.8 2017/06/12 18:55:42 schwarze Exp $ */
 /*
  * Copyright (c) 2009, 2011 Kristaps Dzonsons <kristaps@bsd.lv>
- * Copyright (c) 2014 Ingo Schwarze <schwarze@openbsd.org>
+ * Copyright (c) 2014, 2015, 2017 Ingo Schwarze <schwarze@openbsd.org>
  *
  * Permission to use, copy, modify, and distribute this software for any
  * purpose with or without fee is hereby granted, provided that the above
@@ -66,7 +66,6 @@ mandoc_malloc(size_t size)
 void *
 mandoc_realloc(void *ptr, size_t size)
 {
-
        ptr = realloc(ptr, size);
        if (ptr == NULL)
                err((int)MANDOCLEVEL_SYSERR, NULL);
@@ -76,13 +75,21 @@ mandoc_realloc(void *ptr, size_t size)
 void *
 mandoc_reallocarray(void *ptr, size_t num, size_t size)
 {
-
        ptr = reallocarray(ptr, num, size);
        if (ptr == NULL)
                err((int)MANDOCLEVEL_SYSERR, NULL);
        return ptr;
 }
 
+void *
+mandoc_recallocarray(void *ptr, size_t oldnum, size_t num, size_t size)
+{
+       ptr = recallocarray(ptr, oldnum, num, size);
+       if (ptr == NULL)
+               err((int)MANDOCLEVEL_SYSERR, NULL);
+       return ptr;
+}
+
 char *
 mandoc_strdup(const char *ptr)
 {
index 05a85f3..f535d85 100644 (file)
@@ -1,7 +1,7 @@
-/*     $OpenBSD: mandoc_aux.h,v 1.8 2017/02/09 17:19:07 schwarze Exp $ */
+/*     $OpenBSD: mandoc_aux.h,v 1.9 2017/06/12 18:55:42 schwarze Exp $ */
 /*
  * Copyright (c) 2009, 2011 Kristaps Dzonsons <kristaps@bsd.lv>
- * Copyright (c) 2014 Ingo Schwarze <schwarze@openbsd.org>
+ * Copyright (c) 2014, 2017 Ingo Schwarze <schwarze@openbsd.org>
  *
  * Permission to use, copy, modify, and distribute this software for any
  * purpose with or without fee is hereby granted, provided that the above
@@ -22,5 +22,6 @@ void           *mandoc_calloc(size_t, size_t);
 void            *mandoc_malloc(size_t);
 void            *mandoc_realloc(void *, size_t);
 void            *mandoc_reallocarray(void *, size_t, size_t);
+void            *mandoc_recallocarray(void *, size_t, size_t, size_t);
 char            *mandoc_strdup(const char *);
 char            *mandoc_strndup(const char *, size_t);
index 1bb94b7..fdd0e47 100644 (file)
@@ -1,4 +1,4 @@
-/*     $OpenBSD: tbl_term.c,v 1.34 2017/06/08 18:11:15 schwarze Exp $ */
+/*     $OpenBSD: tbl_term.c,v 1.35 2017/06/12 18:55:42 schwarze Exp $ */
 /*
  * Copyright (c) 2009, 2011 Kristaps Dzonsons <kristaps@bsd.lv>
  * Copyright (c) 2011,2012,2014,2015,2017 Ingo Schwarze <schwarze@openbsd.org>
@@ -66,12 +66,13 @@ term_tbl(struct termp *tp, const struct tbl_span *sp)
        const struct tbl_cell   *cp;
        const struct tbl_dat    *dp;
        static size_t            offset;
-       size_t                   tsz;
-       int                      ic, horiz, spans, vert;
+       size_t                   coloff, tsz;
+       int                      ic, horiz, spans, vert, more;
+       char                     fc;
 
        /* Inhibit printing of spaces: we do padding ourselves. */
 
-       tp->flags |= TERMP_NOSPACE | TERMP_NONOSPACE | TERMP_BRNEVER;
+       tp->flags |= TERMP_NOSPACE | TERMP_NONOSPACE;
 
        /*
         * The first time we're invoked for a given table block,
@@ -109,82 +110,181 @@ term_tbl(struct termp *tp, const struct tbl_span *sp)
                        tbl_hrule(tp, sp, 1);
        }
 
-       /* Vertical frame at the start of each row. */
+       /* Set up the columns. */
 
-       horiz = sp->pos == TBL_SPAN_HORIZ || sp->pos == TBL_SPAN_DHORIZ;
+       tp->flags |= TERMP_MULTICOL;
+       horiz = 0;
+       switch (sp->pos) {
+       case TBL_SPAN_HORIZ:
+       case TBL_SPAN_DHORIZ:
+               horiz = 1;
+               term_setcol(tp, 1);
+               break;
+       case TBL_SPAN_DATA:
+               term_setcol(tp, sp->opts->cols + 2);
+               coloff = tp->tcol->offset;
 
-       if (sp->layout->vert ||
-           (sp->prev != NULL && sp->prev->layout->vert) ||
-           sp->opts->opts & (TBL_OPT_BOX | TBL_OPT_DBOX))
-               term_word(tp, horiz ? "+" : "|");
-       else if (sp->opts->lvert)
-               tbl_char(tp, horiz ? '-' : ASCII_NBRSP, 1);
+               /* Set up a column for a left vertical frame. */
 
-       /*
-        * Now print the actual data itself depending on the span type.
-        * Match data cells to column numbers.
-        */
+               if (sp->opts->opts & (TBL_OPT_BOX | TBL_OPT_DBOX) ||
+                   sp->opts->lvert)
+                       coloff++;
+               tp->tcol->rmargin = coloff;
+
+               /* Set up the data columns. */
 
-       if (sp->pos == TBL_SPAN_DATA) {
-               cp = sp->layout->first;
                dp = sp->first;
                spans = 0;
                for (ic = 0; ic < sp->opts->cols; ic++) {
+                       if (spans == 0) {
+                               tp->tcol++;
+                               tp->tcol->offset = coloff;
+                       }
+                       coloff += tp->tbl.cols[ic].width;
+                       tp->tcol->rmargin = coloff;
+                       coloff++;
+                       if (ic + 1 < sp->opts->cols)
+                               coloff += 2;
+                       if (spans) {
+                               spans--;
+                               continue;
+                       }
+                       if (dp == NULL)
+                               continue;
+                       spans = dp->spans;
+                       dp = dp->next;
+               }
 
-                       /*
-                        * Remeber whether we need a vertical bar
-                        * after this cell.
-                        */
+               /* Set up a column for a right vertical frame. */
 
-                       vert = cp == NULL ? 0 : cp->vert;
+               tp->tcol++;
+               tp->tcol->offset = coloff;
+               if (sp->opts->opts & (TBL_OPT_BOX | TBL_OPT_DBOX) ||
+                   sp->opts->rvert)
+                       coloff++;
+               tp->tcol->rmargin = coloff;
 
-                       /*
-                        * Print the data and advance to the next cell.
-                        */
+               /* Spans may have reduced the number of columns. */
 
-                       if (spans == 0) {
-                               tbl_data(tp, sp->opts, dp, tp->tbl.cols + ic);
-                               if (dp != NULL) {
-                                       spans = dp->spans;
-                                       dp = dp->next;
-                               }
-                       } else
-                               spans--;
-                       if (cp != NULL)
-                               cp = cp->next;
+               tp->lasttcol = tp->tcol - tp->tcols;
 
-                       /*
-                        * Separate columns, except in the middle
-                        * of spans and after the last cell.
-                        */
+               /* Fill the buffers for all data columns. */
 
-                       if (ic + 1 == sp->opts->cols || spans)
+               tp->tcol = tp->tcols;
+               dp = sp->first;
+               spans = 0;
+               for (ic = 0; ic < sp->opts->cols; ic++) {
+                       if (spans) {
+                               spans--;
+                               continue;
+                       }
+                       tp->tcol++;
+                       tp->col = 0;
+                       tbl_data(tp, sp->opts, dp, tp->tbl.cols + ic);
+                       if (dp == NULL)
                                continue;
+                       spans = dp->spans;
+                       dp = dp->next;
+               }
+               break;
+       }
 
-                       tbl_char(tp, ASCII_NBRSP, 1);
-                       if (vert > 0)
-                               tbl_char(tp, '|', vert);
-                       if (vert < 2)
-                               tbl_char(tp, ASCII_NBRSP, 2 - vert);
+       do {
+               /* Print the vertical frame at the start of each row. */
+
+               tp->tcol = tp->tcols;
+               fc = '\0';
+               if (sp->layout->vert ||
+                   (sp->prev != NULL && sp->prev->layout->vert) ||
+                   sp->opts->opts & (TBL_OPT_BOX | TBL_OPT_DBOX))
+                       fc = horiz ? '+' : '|';
+               else if (horiz && sp->opts->lvert)
+                       fc = '-';
+               if (fc != '\0') {
+                       (*tp->advance)(tp, tp->tcols->offset);
+                       (*tp->letter)(tp, fc);
+                       tp->viscol = tp->tcol->offset + 1;
                }
-       } else if (horiz)
-               tbl_hrule(tp, sp, 0);
 
-       /* Vertical frame at the end of each row. */
+               /* Print the data cells. */
+
+               more = 0;
+               if (horiz) {
+                       tbl_hrule(tp, sp, 0);
+                       term_flushln(tp);
+               } else {
+                       cp = sp->layout->first;
+                       dp = sp->first;
+                       spans = 0;
+                       for (ic = 0; ic < sp->opts->cols; ic++) {
+                               if (spans == 0) {
+                                       tp->tcol++;
+                                       if (dp != NULL) {
+                                               spans = dp->spans;
+                                               dp = dp->next;
+                                       }
+                                       if (tp->tcol->col < tp->tcol->lastcol)
+                                               term_flushln(tp);
+                                       if (tp->tcol->col < tp->tcol->lastcol)
+                                               more = 1;
+                                       if (tp->tcol + 1 ==
+                                           tp->tcols + tp->lasttcol)
+                                               continue;
+                               } else
+                                       spans--;
+
+                               /* Vertical frames between data cells. */
+
+                               if (cp != NULL) {
+                                       vert = cp->vert;
+                                       cp = cp->next;
+                               } else
+                                       vert = 0;
+                               if (vert == 0)
+                                       continue;
+
+                               if (tp->tcol->rmargin + 1 > tp->viscol) {
+                                       (*tp->advance)(tp, tp->tcol->rmargin
+                                          + 1 - tp->viscol);
+                                       tp->viscol = tp->tcol->rmargin + 1;
+                               }
+                               while (vert--) {
+                                       (*tp->letter)(tp, '|');
+                                       tp->viscol++;
+                               }
+                       }
+               }
 
-       if (sp->layout->last->vert ||
-           (sp->prev != NULL && sp->prev->layout->last->vert) ||
-           (sp->opts->opts & (TBL_OPT_BOX | TBL_OPT_DBOX)))
-               term_word(tp, horiz ? "+" : " |");
-       else if (sp->opts->rvert)
-               tbl_char(tp, horiz ? '-' : ASCII_NBRSP, 1);
-       term_flushln(tp);
+               /* Print the vertical frame at the end of each row. */
+
+               fc = '\0';
+               if (sp->layout->last->vert ||
+                   (sp->prev != NULL && sp->prev->layout->last->vert) ||
+                   (sp->opts->opts & (TBL_OPT_BOX | TBL_OPT_DBOX)))
+                       fc = horiz ? '+' : '|';
+               else if (horiz && sp->opts->rvert)
+                       fc = '-';
+               if (fc != '\0') {
+                       if (horiz == 0) {
+                               tp->tcol++;
+                               (*tp->advance)(tp,
+                                   tp->tcol->offset > tp->viscol ?
+                                   tp->tcol->offset - tp->viscol : 1);
+                       }
+                       (*tp->letter)(tp, fc);
+               }
+               (*tp->endline)(tp);
+               tp->viscol = 0;
+       } while (more);
 
        /*
         * If we're the last row, clean up after ourselves: clear the
         * existing table configuration and set it to NULL.
         */
 
+       term_setcol(tp, 1);
+       tp->flags &= ~TERMP_MULTICOL;
+       tp->tcol->rmargin = tp->maxrmargin;
        if (sp->next == NULL) {
                if (sp->opts->opts & (TBL_OPT_DBOX | TBL_OPT_BOX)) {
                        tbl_hrule(tp, sp, 1);
@@ -199,7 +299,7 @@ term_tbl(struct termp *tp, const struct tbl_span *sp)
                tp->tbl.cols = NULL;
                tp->tcol->offset = offset;
        }
-       tp->flags &= ~(TERMP_NONOSPACE | TERMP_BRNEVER);
+       tp->flags &= ~TERMP_NONOSPACE;
 }
 
 /*
index 9aa69c7..07b7f57 100644 (file)
@@ -1,4 +1,4 @@
-/*     $OpenBSD: term.c,v 1.128 2017/06/08 12:54:40 schwarze Exp $ */
+/*     $OpenBSD: term.c,v 1.129 2017/06/12 18:55:42 schwarze Exp $ */
 /*
  * Copyright (c) 2008, 2009, 2010, 2011 Kristaps Dzonsons <kristaps@bsd.lv>
  * Copyright (c) 2010-2017 Ingo Schwarze <schwarze@openbsd.org>
@@ -37,6 +37,18 @@ static       void             encode1(struct termp *, int);
 static void             endline(struct termp *);
 
 
+void
+term_setcol(struct termp *p, size_t maxtcol)
+{
+       if (maxtcol > p->maxtcol) {
+               p->tcols = mandoc_recallocarray(p->tcols,
+                   p->maxtcol, maxtcol, sizeof(*p->tcols));
+               p->maxtcol = maxtcol;
+       }
+       p->lasttcol = maxtcol - 1;
+       p->tcol = p->tcols;
+}
+
 void
 term_free(struct termp *p)
 {
@@ -114,9 +126,9 @@ term_flushln(struct termp *p)
            p->maxrmargin - p->viscol - vbl : 0;
        vis = vend = 0;
 
-       if (p->lasttcol == 0)
+       if ((p->flags && TERMP_MULTICOL) == 0)
                p->tcol->col = 0;
-       while (p->tcol->col < p->lastcol) {
+       while (p->tcol->col < p->tcol->lastcol) {
 
                /*
                 * Handle literal tab characters: collapse all
@@ -124,7 +136,7 @@ term_flushln(struct termp *p)
                 */
 
                ntab = 0;
-               while (p->tcol->col < p->lastcol &&
+               while (p->tcol->col < p->tcol->lastcol &&
                    p->tcol->buf[p->tcol->col] == '\t') {
                        vend = term_tab_next(vis);
                        vbl += vend - vis;
@@ -141,7 +153,7 @@ term_flushln(struct termp *p)
                 */
 
                jhy = 0;
-               for (j = p->tcol->col; j < p->lastcol; j++) {
+               for (j = p->tcol->col; j < p->tcol->lastcol; j++) {
                        if (p->tcol->buf[j] == ' ' || p->tcol->buf[j] == '\t')
                                break;
 
@@ -176,7 +188,7 @@ term_flushln(struct termp *p)
 
                if (vend > bp && jhy == 0 && vis > 0 &&
                    (p->flags & TERMP_BRNEVER) == 0) {
-                       if (p->lasttcol)
+                       if (p->flags & TERMP_MULTICOL)
                                return;
 
                        endline(p);
@@ -204,14 +216,14 @@ term_flushln(struct termp *p)
                 * Write out the rest of the word.
                 */
 
-               for ( ; p->tcol->col < p->lastcol; p->tcol->col++) {
+               for ( ; p->tcol->col < p->tcol->lastcol; p->tcol->col++) {
                        if (vend > bp && jhy > 0 && p->tcol->col > jhy)
                                break;
                        if (p->tcol->buf[p->tcol->col] == '\t')
                                break;
                        if (p->tcol->buf[p->tcol->col] == ' ') {
                                j = p->tcol->col;
-                               while (p->tcol->col < p->lastcol &&
+                               while (p->tcol->col < p->tcol->lastcol &&
                                    p->tcol->buf[p->tcol->col] == ' ')
                                        p->tcol->col++;
                                dv = (p->tcol->col - j) * (*p->width)(p, ' ');
@@ -258,10 +270,13 @@ term_flushln(struct termp *p)
        else
                vis = 0;
 
-       p->col = p->lastcol = 0;
+       p->col = p->tcol->col = p->tcol->lastcol = 0;
        p->minbl = p->trailspace;
        p->flags &= ~(TERMP_BACKAFTER | TERMP_BACKBEFORE | TERMP_NOPAD);
 
+       if (p->flags & TERMP_MULTICOL)
+               return;
+
        /* Trailing whitespace is significant in some columns. */
 
        if (vis && vbl && (TERMP_BRTRSP & p->flags))
@@ -303,7 +318,7 @@ term_newln(struct termp *p)
 {
 
        p->flags |= TERMP_NOSPACE;
-       if (p->lastcol || p->viscol)
+       if (p->tcol->lastcol || p->viscol)
                term_flushln(p);
 }
 
@@ -563,12 +578,12 @@ term_word(struct termp *p, const char *word)
                                }
                        }
                        /* Trim trailing backspace/blank pair. */
-                       if (p->lastcol > 2 &&
-                           (p->tcol->buf[p->lastcol - 1] == ' ' ||
-                            p->tcol->buf[p->lastcol - 1] == '\t'))
-                               p->lastcol -= 2;
-                       if (p->col > p->lastcol)
-                               p->col = p->lastcol;
+                       if (p->tcol->lastcol > 2 &&
+                           (p->tcol->buf[p->tcol->lastcol - 1] == ' ' ||
+                            p->tcol->buf[p->tcol->lastcol - 1] == '\t'))
+                               p->tcol->lastcol -= 2;
+                       if (p->col > p->tcol->lastcol)
+                               p->col = p->tcol->lastcol;
                        continue;
                default:
                        continue;
@@ -611,10 +626,10 @@ bufferc(struct termp *p, char c)
        }
        if (p->col + 1 >= p->tcol->maxcols)
                adjbuf(p->tcol, p->col + 1);
-       if (p->lastcol <= p->col || (c != ' ' && c != ASCII_NBRSP))
+       if (p->tcol->lastcol <= p->col || (c != ' ' && c != ASCII_NBRSP))
                p->tcol->buf[p->col] = c;
-       if (p->lastcol < ++p->col)
-               p->lastcol = p->col;
+       if (p->tcol->lastcol < ++p->col)
+               p->tcol->lastcol = p->col;
 }
 
 /*
@@ -657,10 +672,10 @@ encode1(struct termp *p, int c)
                        p->tcol->buf[p->col++] = c;
                p->tcol->buf[p->col++] = '\b';
        }
-       if (p->lastcol <= p->col || (c != ' ' && c != ASCII_NBRSP))
+       if (p->tcol->lastcol <= p->col || (c != ' ' && c != ASCII_NBRSP))
                p->tcol->buf[p->col] = c;
-       if (p->lastcol < ++p->col)
-               p->lastcol = p->col;
+       if (p->tcol->lastcol < ++p->col)
+               p->tcol->lastcol = p->col;
        if (p->flags & TERMP_BACKAFTER) {
                p->flags |= TERMP_BACKBEFORE;
                p->flags &= ~TERMP_BACKAFTER;
@@ -686,7 +701,7 @@ encode(struct termp *p, const char *word, size_t sz)
                    isgraph((unsigned char)word[i]))
                        encode1(p, word[i]);
                else {
-                       if (p->lastcol <= p->col ||
+                       if (p->tcol->lastcol <= p->col ||
                            (word[i] != ' ' && word[i] != ASCII_NBRSP))
                                p->tcol->buf[p->col] = word[i];
                        p->col++;
@@ -703,8 +718,8 @@ encode(struct termp *p, const char *word, size_t sz)
                        }
                }
        }
-       if (p->lastcol < p->col)
-               p->lastcol = p->col;
+       if (p->tcol->lastcol < p->col)
+               p->tcol->lastcol = p->col;
 }
 
 void
index 2d6a12e..ffbeb24 100644 (file)
@@ -1,4 +1,4 @@
-/*     $OpenBSD: term.h,v 1.70 2017/06/07 20:01:07 schwarze Exp $ */
+/*     $OpenBSD: term.h,v 1.71 2017/06/12 18:55:42 schwarze Exp $ */
 /*
  * Copyright (c) 2008, 2009, 2010, 2011 Kristaps Dzonsons <kristaps@bsd.lv>
  * Copyright (c) 2011-2015, 2017 Ingo Schwarze <schwarze@openbsd.org>
@@ -52,6 +52,7 @@ struct        termp_tbl {
 struct termp_col {
        int              *buf;          /* Output buffer. */
        size_t            maxcols;      /* Allocated bytes in buf. */
+       size_t            lastcol;      /* Last byte in buf. */
        size_t            col;          /* Byte in buf to be written. */
        size_t            rmargin;      /* Current right margin. */
        size_t            offset;       /* Current left margin. */
@@ -69,7 +70,6 @@ struct        termp {
        size_t            lastrmargin;  /* Right margin before the last ll. */
        size_t            maxrmargin;   /* Max right margin. */
        size_t            col;          /* Byte position in buf. */
-       size_t            lastcol;      /* Bytes in buf. */
        size_t            viscol;       /* Chars on current line. */
        size_t            trailspace;   /* See term_flushln(). */
        size_t            minbl;        /* Minimum blanks before next field. */
@@ -98,6 +98,7 @@ struct        termp {
 #define        TERMP_NOBUF      (1 << 17)      /* Bypass output buffer. */
 #define        TERMP_NEWMC      (1 << 18)      /* No .mc printed yet. */
 #define        TERMP_ENDMC      (1 << 19)      /* Next break ends .mc mode. */
+#define        TERMP_MULTICOL   (1 << 20)      /* Multiple column mode. */
        enum termtype     type;         /* Terminal, PS, or PDF. */
        enum termenc      enc;          /* Type of encoding. */
        enum termfont     fontl;        /* Last font set. */
@@ -128,6 +129,7 @@ void                  roff_term_pre(struct termp *, const struct roff_node *);
 void             term_eqn(struct termp *, const struct eqn *);
 void             term_tbl(struct termp *, const struct tbl_span *);
 void             term_free(struct termp *);
+void             term_setcol(struct termp *, size_t);
 void             term_newln(struct termp *);
 void             term_vspace(struct termp *);
 void             term_word(struct termp *, const char *);