tech@mandoc.bsd.lv
 help / color / mirror / Atom feed
From: Ingo Schwarze <schwarze@usta.de>
To: tech@mdocml.bsd.lv
Subject: TERMP_NOLPAD r.i.p.
Date: Mon, 19 Sep 2011 23:39:39 +0200	[thread overview]
Message-ID: <20110919213939.GA19736@iris.usta.de> (raw)
In-Reply-To: <4E765AFE.9070302@bsd.lv>

Hi,

Kristaps drew my attention to the fact that the popt(3) manual
is badly broken:

> .HP
> .nf
> .BI "int poptReadDefaultConfig(poptContext " con ", int " flags ");"
> .fi
> 
> This looks fine in `ul', but on the raw screen it prints out lots of
> blank space due to the flush.  Of course, it's because rmargin is
> set to TERM_MAXMARGIN and the TERMP_NOBREAK goes nuts (in
> print_man_node()). This can be hacked around by NOT setting rmargin
> and only setting maxrmargin, but it still looks crappy.  Any
> thoughts?

Yes, i have come up with the following diff which might be one of the
most beautiful i have written for mandoc so far:  I suggest to
enhance functionality and squash bugs - by removing a flag!

Specifically, as long as we have TERMP_NOLPAD, the problem at hand
is very hard to solve, in particular in the following variant:

  .nf
  .HP 12n
  This is a very long tag.
  .fi
  This is the indented text; it is of considerable length as well.

With TERMP_NOLPAD, while rendering the first line of the .HP,
term_flushln() - yes, once again my favourite function -
overruns the column, breaks the line, and pads to the indent.
After that, the .fi breaks the already padded line - ouch.

To improve this, we should not pad until we know that we want
to write actual content to a line, but let the next call to
term_flushln() do the padding instead; which means that we don't
need TERMP_NOLPAD any longer.

Some of the chunks are non-obvious:

 * term.c chunk @@ -130,9 +126,10 @@:
   Always calculate the padding before starting a new line.
   No more TERMP_NOLPAD.
 * term.c chunk @@ -232,10 +229,14 @@:
   An independent bugfix.  Backspace has negative length.
   Without this, indentation does not get right.
 * term.c chunk @@ -244,7 +245,8 @@:
   Handle the edge case of an empty input string:
   In that case, vis is still zero, and there is no need to go back.
   This occurs e.g. in man(7) page headers and footers.
 * term.c chunk @@ -269,25 +271,18 @@:
   This is the main point.
   No more padding at the end of term_flushln().
 * Most other chunks just drop the obsolete flag.
 * man_term.c chunk @@ -241,6 +241,18 @@:
   That's the main fix for .HP indentation containing .fi/.nf,
   and the only ugly chunk in this diff.
   See the comment in the code what it does.
 * man_term.c chunk @@ -452,8 +464,11 @@:
   This adjusts the indentation width to what groff does.
 * man_term.c chunk @@ -878,7 +889,7 @@:
   The right margin must not be unlimited in literal mode
   when TERMP_NOBREAK is in effect.  That avoids the long string
   of blank characters.

This patch is -65 +50 :).

It reduces groff/mandoc differences in base by nearly 20%,
from 89k to 72k lines of diffs.

I see no regressions.

OK?
  Ingo


Index: term.h
===================================================================
RCS file: /cvs/src/usr.bin/mandoc/term.h,v
retrieving revision 1.31
diff -u -p -r1.31 term.h
--- term.h	18 Sep 2011 10:25:28 -0000	1.31
+++ term.h	19 Sep 2011 21:29:38 -0000
@@ -64,7 +64,6 @@ struct	termp {
 	int		  flags;
 #define	TERMP_SENTENCE	 (1 << 1)	/* Space before a sentence. */
 #define	TERMP_NOSPACE	 (1 << 2)	/* No space before words. */
-#define	TERMP_NOLPAD	 (1 << 3)	/* See term_flushln(). */
 #define	TERMP_NOBREAK	 (1 << 4)	/* See term_flushln(). */
 #define	TERMP_IGNDELIM	 (1 << 6)	/* Delims like regulars. */
 #define	TERMP_NONOSPACE	 (1 << 7)	/* No space (no autounset). */
Index: term.c
===================================================================
RCS file: /cvs/src/usr.bin/mandoc/term.c,v
retrieving revision 1.60
diff -u -p -r1.60 term.c
--- term.c	18 Sep 2011 20:38:02 -0000	1.60
+++ term.c	19 Sep 2011 21:29:38 -0000
@@ -75,22 +75,18 @@ term_end(struct termp *p)
  *
  * The following flags may be specified:
  *
- *  - TERMP_NOLPAD: when beginning to write the line, don't left-pad the
- *    offset value.  This is useful when doing columnar lists where the
- *    prior column has right-padded.
- *
  *  - TERMP_NOBREAK: this is the most important and is used when making
- *    columns.  In short: don't print a newline and instead pad to the
- *    right margin.  Used in conjunction with TERMP_NOLPAD.
+ *    columns.  In short: don't print a newline and instead expect the
+ *    next call to do the padding up to the start of the next column.
  *
- *  - TERMP_TWOSPACE: when padding, make sure there are at least two
- *    space characters of padding.  Otherwise, rather break the line.
+ *  - TERMP_TWOSPACE: make sure there is room for at least two space
+ *    characters of padding.  Otherwise, rather break the line.
  *
  *  - TERMP_DANGLE: don't newline when TERMP_NOBREAK is specified and
  *    the line is overrun, and don't pad-right if it's underrun.
  *
  *  - TERMP_HANG: like TERMP_DANGLE, but doesn't newline when
- *    overruning, instead save the position and continue at that point
+ *    overrunning, instead save the position and continue at that point
  *    when the next invocation.
  *
  *  In-line line breaking:
@@ -130,9 +126,10 @@ term_flushln(struct termp *p)
 	bp = TERMP_NOBREAK & p->flags ? mmax : maxvis;
 
 	/*
-	 * Indent the first line of a paragraph.
+	 * Calculate the required amount of padding.
 	 */
-	vbl = p->flags & TERMP_NOLPAD ? (size_t)0 : p->offset;
+	vbl = p->offset + p->overstep > p->viscol ?
+	      p->offset + p->overstep - p->viscol : 0;
 
 	vis = vend = 0;
 	i = 0;
@@ -232,10 +229,14 @@ term_flushln(struct termp *p)
 			if (ASCII_HYPH == p->buf[i]) {
 				(*p->letter)(p, '-');
 				p->viscol += (*p->width)(p, '-');
-			} else {
-				(*p->letter)(p, p->buf[i]);
-				p->viscol += (*p->width)(p, p->buf[i]);
+				continue;
 			}
+
+			(*p->letter)(p, p->buf[i]);
+			if (8 == p->buf[i])
+				p->viscol -= (*p->width)(p, p->buf[i-1]);
+			else 
+				p->viscol += (*p->width)(p, p->buf[i]);
 		}
 		vis = vend;
 	}
@@ -244,7 +245,8 @@ term_flushln(struct termp *p)
 	 * If there was trailing white space, it was not printed;
 	 * so reset the cursor position accordingly.
 	 */
-	vis -= vbl;
+	if (vis)
+		vis -= vbl;
 
 	p->col = 0;
 	p->overstep = 0;
@@ -269,25 +271,18 @@ term_flushln(struct termp *p)
 		 * move it one step LEFT and flag the rest of the line
 		 * to be longer.
 		 */
-		if (p->overstep >= -1) {
-			assert((int)maxvis + p->overstep >= 0);
-			maxvis += (size_t)p->overstep;
-		} else
+		if (p->overstep < -1)
 			p->overstep = 0;
+		return;
 
 	} else if (TERMP_DANGLE & p->flags)
 		return;
 
-	/* Right-pad. */
-	if (maxvis > vis +
+	/* If the column was overrun, break the line. */
+	if (maxvis <= vis +
 	    ((TERMP_TWOSPACE & p->flags) ? (*p->width)(p, ' ') : 0)) {
-		p->viscol += maxvis - vis;
-		(*p->advance)(p, maxvis - vis);
-		vis += (maxvis - vis);
-	} else {	/* ...or newline break. */
 		(*p->endline)(p);
-		p->viscol = p->rmargin;
-		(*p->advance)(p, p->rmargin);
+		p->viscol = 0;
 	}
 }
 
@@ -302,12 +297,8 @@ term_newln(struct termp *p)
 {
 
 	p->flags |= TERMP_NOSPACE;
-	if (0 == p->col && 0 == p->viscol) {
-		p->flags &= ~TERMP_NOLPAD;
-		return;
-	}
-	term_flushln(p);
-	p->flags &= ~TERMP_NOLPAD;
+	if (p->col || p->viscol)
+		term_flushln(p);
 }
 
 
Index: mdoc_term.c
===================================================================
RCS file: /cvs/src/usr.bin/mandoc/mdoc_term.c,v
retrieving revision 1.135
diff -u -p -r1.135 mdoc_term.c
--- mdoc_term.c	18 Sep 2011 10:25:28 -0000	1.135
+++ mdoc_term.c	19 Sep 2011 21:29:38 -0000
@@ -431,7 +431,7 @@ print_mdoc_foot(struct termp *p, const v
 
 	p->offset = p->rmargin;
 	p->rmargin = p->maxrmargin - term_strlen(p, m->os);
-	p->flags |= TERMP_NOLPAD | TERMP_NOSPACE;
+	p->flags |= TERMP_NOSPACE;
 
 	term_word(p, m->date);
 	term_flushln(p);
@@ -439,7 +439,7 @@ print_mdoc_foot(struct termp *p, const v
 	p->offset = p->rmargin;
 	p->rmargin = p->maxrmargin;
 	p->flags &= ~TERMP_NOBREAK;
-	p->flags |= TERMP_NOLPAD | TERMP_NOSPACE;
+	p->flags |= TERMP_NOSPACE;
 
 	term_word(p, m->os);
 	term_flushln(p);
@@ -495,7 +495,7 @@ print_mdoc_head(struct termp *p, const v
 
 	p->offset = p->rmargin;
 	p->rmargin = p->maxrmargin - term_strlen(p, title);
-	p->flags |= TERMP_NOLPAD | TERMP_NOSPACE;
+	p->flags |= TERMP_NOSPACE;
 
 	term_word(p, buf);
 	term_flushln(p);
@@ -503,7 +503,7 @@ print_mdoc_head(struct termp *p, const v
 	p->offset = p->rmargin;
 	p->rmargin = p->maxrmargin;
 	p->flags &= ~TERMP_NOBREAK;
-	p->flags |= TERMP_NOLPAD | TERMP_NOSPACE;
+	p->flags |= TERMP_NOSPACE;
 
 	term_word(p, title);
 	term_flushln(p);
@@ -783,16 +783,11 @@ termp_it_pre(DECL_ARGS)
 	case (LIST_hyphen):
 		if (MDOC_HEAD == n->type)
 			p->flags |= TERMP_NOBREAK;
-		else
-			p->flags |= TERMP_NOLPAD;
 		break;
 	case (LIST_hang):
 		if (MDOC_HEAD == n->type)
 			p->flags |= TERMP_NOBREAK;
 		else
-			p->flags |= TERMP_NOLPAD;
-
-		if (MDOC_HEAD != n->type)
 			break;
 
 		/*
@@ -803,17 +798,14 @@ termp_it_pre(DECL_ARGS)
 		 */
 		if (n->next->child && 
 				(MDOC_Bl == n->next->child->tok ||
-				 MDOC_Bd == n->next->child->tok)) {
+				 MDOC_Bd == n->next->child->tok))
 			p->flags &= ~TERMP_NOBREAK;
-			p->flags &= ~TERMP_NOLPAD;
-		} else
+		else
 			p->flags |= TERMP_HANG;
 		break;
 	case (LIST_tag):
 		if (MDOC_HEAD == n->type)
 			p->flags |= TERMP_NOBREAK | TERMP_TWOSPACE;
-		else
-			p->flags |= TERMP_NOLPAD;
 
 		if (MDOC_HEAD != n->type)
 			break;
@@ -829,10 +821,6 @@ termp_it_pre(DECL_ARGS)
 		else
 			p->flags |= TERMP_NOBREAK;
 
-		assert(n->prev);
-		if (MDOC_BODY == n->prev->type) 
-			p->flags |= TERMP_NOLPAD;
-
 		break;
 	case (LIST_diag):
 		if (MDOC_HEAD == n->type)
@@ -989,7 +977,6 @@ termp_it_post(DECL_ARGS)
 	p->flags &= ~TERMP_DANGLE;
 	p->flags &= ~TERMP_NOBREAK;
 	p->flags &= ~TERMP_TWOSPACE;
-	p->flags &= ~TERMP_NOLPAD;
 	p->flags &= ~TERMP_HANG;
 }
 
@@ -1005,7 +992,7 @@ termp_nm_pre(DECL_ARGS)
 	if (MDOC_BODY == n->type) {
 		if (NULL == n->child)
 			return(0);
-		p->flags |= TERMP_NOLPAD | TERMP_NOSPACE;
+		p->flags |= TERMP_NOSPACE;
 		p->offset += term_len(p, 1) +
 		    (NULL == n->prev->child ? term_strlen(p, m->name) :
 		     MDOC_TEXT == n->prev->child->type ?
@@ -1050,10 +1037,8 @@ termp_nm_post(DECL_ARGS)
 	if (MDOC_HEAD == n->type && n->next->child) {
 		term_flushln(p);
 		p->flags &= ~(TERMP_NOBREAK | TERMP_HANG);
-	} else if (MDOC_BODY == n->type && n->child) {
+	} else if (MDOC_BODY == n->type && n->child)
 		term_flushln(p);
-		p->flags &= ~TERMP_NOLPAD;
-	}
 }
 
 		
Index: man_term.c
===================================================================
RCS file: /cvs/src/usr.bin/mandoc/man_term.c,v
retrieving revision 1.71
diff -u -p -r1.71 man_term.c
--- man_term.c	18 Sep 2011 10:25:28 -0000	1.71
+++ man_term.c	19 Sep 2011 21:29:38 -0000
@@ -241,6 +241,18 @@ pre_literal(DECL_ARGS)
 	else
 		mt->fl &= ~MANT_LITERAL;
 
+	/*
+	 * Unlike .IP and .TP, .HP does not have a HEAD.
+	 * So in case a second call to term_flushln() is needed,
+	 * indentation has to be set up explicitly.
+	 */
+	if (MAN_HP == n->parent->tok && p->rmargin < p->maxrmargin) {
+		p->offset = p->rmargin + 1;
+		p->rmargin = p->maxrmargin;
+		p->flags &= ~(TERMP_NOBREAK | TERMP_TWOSPACE);
+		p->flags |= TERMP_NOSPACE;
+	}
+
 	return(0);
 }
 
@@ -427,7 +439,7 @@ pre_sp(DECL_ARGS)
 static int
 pre_HP(DECL_ARGS)
 {
-	size_t			 len;
+	size_t			 len, one;
 	int			 ival;
 	const struct man_node	*nn;
 
@@ -452,8 +464,11 @@ pre_HP(DECL_ARGS)
 		if ((ival = a2width(p, nn->string)) >= 0)
 			len = (size_t)ival;
 
-	if (0 == len)
-		len = term_len(p, 1);
+	one = term_len(p, 1);
+	if (len > one)
+		len -= one;
+	else
+		len = one;
 
 	p->offset = mt->offset;
 	p->rmargin = mt->offset + len;
@@ -516,7 +531,6 @@ pre_IP(DECL_ARGS)
 
 	switch (n->type) {
 	case (MAN_BODY):
-		p->flags |= TERMP_NOLPAD;
 		p->flags |= TERMP_NOSPACE;
 		break;
 	case (MAN_HEAD):
@@ -587,7 +601,6 @@ post_IP(DECL_ARGS)
 		break;
 	case (MAN_BODY):
 		term_newln(p);
-		p->flags &= ~TERMP_NOLPAD;
 		break;
 	default:
 		break;
@@ -608,7 +621,6 @@ pre_TP(DECL_ARGS)
 		p->flags |= TERMP_NOBREAK;
 		break;
 	case (MAN_BODY):
-		p->flags |= TERMP_NOLPAD;
 		p->flags |= TERMP_NOSPACE;
 		break;
 	case (MAN_BLOCK):
@@ -677,7 +689,6 @@ post_TP(DECL_ARGS)
 		break;
 	case (MAN_BODY):
 		term_newln(p);
-		p->flags &= ~TERMP_NOLPAD;
 		break;
 	default:
 		break;
@@ -878,7 +889,7 @@ print_man_node(DECL_ARGS)
 		 * -man doesn't have nested macros, we don't need to be
 		 * more specific than this.
 		 */
-		if (MANT_LITERAL & mt->fl && 
+		if (MANT_LITERAL & mt->fl && ! (TERMP_NOBREAK & p->flags) &&
 				(NULL == n->next || 
 				 n->next->line > n->line)) {
 			rm = p->rmargin;
@@ -886,7 +897,6 @@ print_man_node(DECL_ARGS)
 			p->rmargin = p->maxrmargin = TERM_MAXMARGIN;
 			p->flags |= TERMP_NOSPACE;
 			term_flushln(p);
-			p->flags &= ~TERMP_NOLPAD;
 			p->rmargin = rm;
 			p->maxrmargin = rmax;
 		}
@@ -968,7 +978,7 @@ print_man_foot(struct termp *p, const vo
 		term_word(p, "");
 	term_flushln(p);
 
-	p->flags |= TERMP_NOLPAD | TERMP_NOSPACE;
+	p->flags |= TERMP_NOSPACE;
 	p->offset = p->rmargin;
 	p->rmargin = p->maxrmargin;
 	p->flags &= ~TERMP_NOBREAK;
@@ -1015,7 +1025,7 @@ print_man_head(struct termp *p, const vo
 	term_word(p, title);
 	term_flushln(p);
 
-	p->flags |= TERMP_NOLPAD | TERMP_NOSPACE;
+	p->flags |= TERMP_NOSPACE;
 	p->offset = p->rmargin;
 	p->rmargin = p->offset + buflen + titlen < p->maxrmargin ?
 	    p->maxrmargin - titlen : p->maxrmargin;
@@ -1025,7 +1035,7 @@ print_man_head(struct termp *p, const vo
 
 	p->flags &= ~TERMP_NOBREAK;
 	if (p->rmargin + titlen <= p->maxrmargin) {
-		p->flags |= TERMP_NOLPAD | TERMP_NOSPACE;
+		p->flags |= TERMP_NOSPACE;
 		p->offset = p->rmargin;
 		p->rmargin = p->maxrmargin;
 		term_word(p, title);
--
 To unsubscribe send an email to tech+unsubscribe@mdocml.bsd.lv

       reply	other threads:[~2011-09-19 21:42 UTC|newest]

Thread overview: 8+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
     [not found] <20110918161317.GE29692@iris.usta.de>
     [not found] ` <4E765AFE.9070302@bsd.lv>
2011-09-19 21:39   ` Ingo Schwarze [this message]
2011-09-19 21:58     ` Kristaps Dzonsons
2011-09-20 11:12       ` Kristaps Dzonsons
2011-09-20 11:14         ` Kristaps Dzonsons
2011-09-20 12:14         ` Ingo Schwarze
2011-09-20 12:26           ` Kristaps Dzonsons
2011-09-20 13:34             ` Ingo Schwarze
2011-09-20 14:05               ` Kristaps Dzonsons

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=20110919213939.GA19736@iris.usta.de \
    --to=schwarze@usta.de \
    --cc=tech@mdocml.bsd.lv \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).