diff options
Diffstat (limited to 'usr/src/cmd/mandoc/term.c')
| -rw-r--r-- | usr/src/cmd/mandoc/term.c | 488 | 
1 files changed, 320 insertions, 168 deletions
| diff --git a/usr/src/cmd/mandoc/term.c b/usr/src/cmd/mandoc/term.c index 1217d473ca..f67fcf9d95 100644 --- a/usr/src/cmd/mandoc/term.c +++ b/usr/src/cmd/mandoc/term.c @@ -1,4 +1,4 @@ -/*	$Id: term.c,v 1.259 2017/01/08 18:16:58 schwarze Exp $ */ +/*	$Id: term.c,v 1.274 2017/07/28 14:25:48 schwarze Exp $ */  /*   * Copyright (c) 2008, 2009, 2010, 2011 Kristaps Dzonsons <kristaps@bsd.lv>   * Copyright (c) 2010-2017 Ingo Schwarze <schwarze@openbsd.org> @@ -32,17 +32,31 @@  #include "main.h"  static	size_t		 cond_width(const struct termp *, int, int *); -static	void		 adjbuf(struct termp *p, size_t); +static	void		 adjbuf(struct termp_col *, size_t);  static	void		 bufferc(struct termp *, char);  static	void		 encode(struct termp *, const char *, size_t);  static	void		 encode1(struct termp *, int); +static	void		 endline(struct termp *);  void -term_free(struct termp *p) +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; +} -	free(p->buf); +void +term_free(struct termp *p) +{ +	for (p->tcol = p->tcols; p->tcol < p->tcols + p->maxtcol; p->tcol++) +		free(p->tcol->buf); +	free(p->tcols);  	free(p->fontq);  	free(p);  } @@ -84,69 +98,54 @@ term_end(struct termp *p)   *    to be broken, start the next line at the right margin instead   *    of at the offset.  Used together with TERMP_NOBREAK for the tags   *    in various kinds of tagged lists. - *  - TERMP_DANGLE: Do not break the output line at the right margin, + *  - TERMP_HANG: Do not break the output line at the right margin,   *    append the next chunk after it even if this one is too long.   *    To be used together with TERMP_NOBREAK. - *  - TERMP_HANG: Like TERMP_DANGLE, and also suppress padding before - *    the next chunk if this column is not full. + *  - TERMP_NOPAD: Start writing at the current position, + *    do not pad with blank characters up to the offset.   */  void  term_flushln(struct termp *p)  { -	size_t		 i;     /* current input position in p->buf */ -	int		 ntab;	/* number of tabs to prepend */  	size_t		 vis;   /* current visual position on output */  	size_t		 vbl;   /* number of blanks to prepend to output */  	size_t		 vend;	/* end of word visual position on output */  	size_t		 bp;    /* visual right border position */  	size_t		 dv;    /* temporary for visual pos calculations */ -	size_t		 j;     /* temporary loop index for p->buf */ +	size_t		 j;     /* temporary loop index for p->tcol->buf */  	size_t		 jhy;	/* last hyph before overflow w/r/t j */  	size_t		 maxvis; /* output position of visible boundary */ - -	/* -	 * First, establish the maximum columns of "visible" content. -	 * This is usually the difference between the right-margin and -	 * an indentation, but can be, for tagged lists or columns, a -	 * small set of values. -	 * -	 * The following unsigned-signed subtractions look strange, -	 * but they are actually correct.  If the int p->overstep -	 * is negative, it gets sign extended.  Subtracting that -	 * very large size_t effectively adds a small number to dv. -	 */ -	dv = p->rmargin > p->offset ? p->rmargin - p->offset : 0; -	maxvis = (int)dv > p->overstep ? dv - (size_t)p->overstep : 0; - -	if (p->flags & TERMP_NOBREAK) { -		dv = p->maxrmargin > p->offset ? -		     p->maxrmargin - p->offset : 0; -		bp = (int)dv > p->overstep ? -		     dv - (size_t)p->overstep : 0; -	} else -		bp = maxvis; - -	/* -	 * Calculate the required amount of padding. -	 */ -	vbl = p->offset + p->overstep > p->viscol ? -	      p->offset + p->overstep - p->viscol : 0; - +	int		 ntab;	/* number of tabs to prepend */ +	int		 breakline; /* after this word */ + +	vbl = (p->flags & TERMP_NOPAD) || p->tcol->offset < p->viscol ? +	    0 : p->tcol->offset - p->viscol; +	if (p->minbl && vbl < p->minbl) +		vbl = p->minbl; +	maxvis = p->tcol->rmargin > p->viscol + vbl ? +	    p->tcol->rmargin - p->viscol - vbl : 0; +	bp = !(p->flags & TERMP_NOBREAK) ? maxvis : +	    p->maxrmargin > p->viscol + vbl ? +	    p->maxrmargin - p->viscol - vbl : 0;  	vis = vend = 0; -	i = 0; -	while (i < p->col) { +	if ((p->flags & TERMP_MULTICOL) == 0) +		p->tcol->col = 0; +	while (p->tcol->col < p->tcol->lastcol) { +  		/*  		 * Handle literal tab characters: collapse all  		 * subsequent tabs into a single huge set of spaces.  		 */ +  		ntab = 0; -		while (i < p->col && '\t' == p->buf[i]) { -			vend = (vis / p->tabwidth + 1) * p->tabwidth; +		while (p->tcol->col < p->tcol->lastcol && +		    p->tcol->buf[p->tcol->col] == '\t') { +			vend = term_tab_next(vis);  			vbl += vend - vis;  			vis = vend;  			ntab++; -			i++; +			p->tcol->col++;  		}  		/* @@ -156,84 +155,98 @@ term_flushln(struct termp *p)  		 * space is printed according to regular spacing rules).  		 */ -		for (j = i, jhy = 0; j < p->col; j++) { -			if (' ' == p->buf[j] || '\t' == p->buf[j]) +		jhy = 0; +		breakline = 0; +		for (j = p->tcol->col; j < p->tcol->lastcol; j++) { +			if (p->tcol->buf[j] == '\n') { +				if ((p->flags & TERMP_BRIND) == 0) +					breakline = 1; +				continue; +			} +			if (p->tcol->buf[j] == ' ' || p->tcol->buf[j] == '\t')  				break;  			/* Back over the last printed character. */ -			if (8 == p->buf[j]) { +			if (p->tcol->buf[j] == '\b') {  				assert(j); -				vend -= (*p->width)(p, p->buf[j - 1]); +				vend -= (*p->width)(p, p->tcol->buf[j - 1]);  				continue;  			}  			/* Regular word. */  			/* Break at the hyphen point if we overrun. */  			if (vend > vis && vend < bp && -			    (ASCII_HYPH == p->buf[j] || -			     ASCII_BREAK == p->buf[j])) +			    (p->tcol->buf[j] == ASCII_HYPH|| +			     p->tcol->buf[j] == ASCII_BREAK))  				jhy = j;  			/*  			 * Hyphenation now decided, put back a real  			 * hyphen such that we get the correct width.  			 */ -			if (ASCII_HYPH == p->buf[j]) -				p->buf[j] = '-'; +			if (p->tcol->buf[j] == ASCII_HYPH) +				p->tcol->buf[j] = '-'; -			vend += (*p->width)(p, p->buf[j]); +			vend += (*p->width)(p, p->tcol->buf[j]);  		}  		/*  		 * Find out whether we would exceed the right margin.  		 * If so, break to the next line.  		 */ -		if (vend > bp && 0 == jhy && vis > 0) { + +		if (vend > bp && jhy == 0 && vis > 0 && +		    (p->flags & TERMP_BRNEVER) == 0) { +			if (p->flags & TERMP_MULTICOL) +				return; + +			endline(p);  			vend -= vis; -			(*p->endline)(p); -			p->viscol = 0; -			if (TERMP_BRIND & p->flags) { -				vbl = p->rmargin; -				vend += p->rmargin; -				vend -= p->offset; -			} else -				vbl = p->offset; -			/* use pending tabs on the new line */ +			/* Use pending tabs on the new line. */ -			if (0 < ntab) -				vbl += ntab * p->tabwidth; +			vbl = 0; +			while (ntab--) +				vbl = term_tab_next(vbl); -			/* -			 * Remove the p->overstep width. -			 * Again, if p->overstep is negative, -			 * sign extension does the right thing. -			 */ +			/* Re-establish indentation. */ -			bp += (size_t)p->overstep; -			p->overstep = 0; +			if (p->flags & TERMP_BRIND) +				vbl += p->tcol->rmargin; +			else +				vbl += p->tcol->offset; +			maxvis = p->tcol->rmargin > vbl ? +			    p->tcol->rmargin - vbl : 0; +			bp = !(p->flags & TERMP_NOBREAK) ? maxvis : +			    p->maxrmargin > vbl ?  p->maxrmargin - vbl : 0;  		} -		/* Write out the [remaining] word. */ -		for ( ; i < p->col; i++) { -			if (vend > bp && jhy > 0 && i > jhy) +		/* +		 * Write out the rest of the word. +		 */ + +		for ( ; p->tcol->col < p->tcol->lastcol; p->tcol->col++) { +			if (vend > bp && jhy > 0 && p->tcol->col > jhy)  				break; -			if ('\t' == p->buf[i]) +			if (p->tcol->buf[p->tcol->col] == '\n') +				continue; +			if (p->tcol->buf[p->tcol->col] == '\t')  				break; -			if (' ' == p->buf[i]) { -				j = i; -				while (i < p->col && ' ' == p->buf[i]) -					i++; -				dv = (i - j) * (*p->width)(p, ' '); +			if (p->tcol->buf[p->tcol->col] == ' ') { +				j = p->tcol->col; +				while (p->tcol->col < p->tcol->lastcol && +				    p->tcol->buf[p->tcol->col] == ' ') +					p->tcol->col++; +				dv = (p->tcol->col - j) * (*p->width)(p, ' ');  				vbl += dv;  				vend += dv;  				break;  			} -			if (ASCII_NBRSP == p->buf[i]) { +			if (p->tcol->buf[p->tcol->col] == ASCII_NBRSP) {  				vbl += (*p->width)(p, ' ');  				continue;  			} -			if (ASCII_BREAK == p->buf[i]) +			if (p->tcol->buf[p->tcol->col] == ASCII_BREAK)  				continue;  			/* @@ -247,61 +260,83 @@ term_flushln(struct termp *p)  				vbl = 0;  			} -			(*p->letter)(p, p->buf[i]); -			if (8 == p->buf[i]) -				p->viscol -= (*p->width)(p, p->buf[i-1]); +			(*p->letter)(p, p->tcol->buf[p->tcol->col]); +			if (p->tcol->buf[p->tcol->col] == '\b') +				p->viscol -= (*p->width)(p, +				    p->tcol->buf[p->tcol->col - 1]);  			else -				p->viscol += (*p->width)(p, p->buf[i]); +				p->viscol += (*p->width)(p, +				    p->tcol->buf[p->tcol->col]);  		}  		vis = vend; + +		if (breakline == 0) +			continue; + +		/* Explicitly requested output line break. */ + +		if (p->flags & TERMP_MULTICOL) +			return; + +		endline(p); +		breakline = 0; +		vis = vend = 0; + +		/* Re-establish indentation. */ + +		vbl = p->tcol->offset; +		maxvis = p->tcol->rmargin > vbl ? +		    p->tcol->rmargin - vbl : 0; +		bp = !(p->flags & TERMP_NOBREAK) ? maxvis : +		    p->maxrmargin > vbl ?  p->maxrmargin - vbl : 0;  	}  	/*  	 * If there was trailing white space, it was not printed;  	 * so reset the cursor position accordingly.  	 */ +  	if (vis > vbl)  		vis -= vbl;  	else  		vis = 0; -	p->col = 0; -	p->overstep = 0; -	p->flags &= ~(TERMP_BACKAFTER | TERMP_BACKBEFORE); +	p->col = p->tcol->col = p->tcol->lastcol = 0; +	p->minbl = p->trailspace; +	p->flags &= ~(TERMP_BACKAFTER | TERMP_BACKBEFORE | TERMP_NOPAD); -	if ( ! (TERMP_NOBREAK & p->flags)) { -		p->viscol = 0; -		(*p->endline)(p); -		return; -	} - -	if (TERMP_HANG & p->flags) { -		p->overstep += (int)(p->offset + vis - p->rmargin + -		    p->trailspace * (*p->width)(p, ' ')); - -		/* -		 * If we have overstepped the margin, temporarily move -		 * it to the right and flag the rest of the line to be -		 * shorter. -		 * If there is a request to keep the columns together, -		 * allow negative overstep when the column is not full. -		 */ -		if (p->trailspace && p->overstep < 0) -			p->overstep = 0; -		return; - -	} else if (TERMP_DANGLE & p->flags) +	if (p->flags & TERMP_MULTICOL)  		return;  	/* Trailing whitespace is significant in some columns. */ +  	if (vis && vbl && (TERMP_BRTRSP & p->flags))  		vis += vbl;  	/* If the column was overrun, break the line. */ -	if (maxvis < vis + p->trailspace * (*p->width)(p, ' ')) { -		(*p->endline)(p); -		p->viscol = 0; +	if ((p->flags & TERMP_NOBREAK) == 0 || +	    ((p->flags & TERMP_HANG) == 0 && +	     vis + p->trailspace * (*p->width)(p, ' ') > maxvis)) +		endline(p); +} + +static void +endline(struct termp *p) +{ +	if ((p->flags & (TERMP_NEWMC | TERMP_ENDMC)) == TERMP_ENDMC) { +		p->mc = NULL; +		p->flags &= ~TERMP_ENDMC; +	} +	if (p->mc != NULL) { +		if (p->viscol && p->maxrmargin >= p->viscol) +			(*p->advance)(p, p->maxrmargin - p->viscol + 1); +		p->flags |= TERMP_NOBUF | TERMP_NOSPACE; +		term_word(p, p->mc); +		p->flags &= ~(TERMP_NOBUF | TERMP_NEWMC);  	} +	p->viscol = 0; +	p->minbl = 0; +	(*p->endline)(p);  }  /* @@ -314,7 +349,7 @@ term_newln(struct termp *p)  {  	p->flags |= TERMP_NOSPACE; -	if (p->col || p->viscol) +	if (p->tcol->lastcol || p->viscol)  		term_flushln(p);  } @@ -330,6 +365,7 @@ term_vspace(struct termp *p)  	term_newln(p);  	p->viscol = 0; +	p->minbl = 0;  	if (0 < p->skipvsp)  		p->skipvsp--;  	else @@ -397,30 +433,31 @@ term_fontpop(struct termp *p)  void  term_word(struct termp *p, const char *word)  { +	struct roffsu	 su;  	const char	 nbrsp[2] = { ASCII_NBRSP, 0 };  	const char	*seq, *cp;  	int		 sz, uc; -	size_t		 ssz; +	size_t		 csz, lsz, ssz;  	enum mandoc_esc	 esc; -	if ( ! (TERMP_NOSPACE & p->flags)) { -		if ( ! (TERMP_KEEP & p->flags)) { -			bufferc(p, ' '); -			if (TERMP_SENTENCE & p->flags) +	if ((p->flags & TERMP_NOBUF) == 0) { +		if ((p->flags & TERMP_NOSPACE) == 0) { +			if ((p->flags & TERMP_KEEP) == 0) {  				bufferc(p, ' '); -		} else -			bufferc(p, ASCII_NBRSP); +				if (p->flags & TERMP_SENTENCE) +					bufferc(p, ' '); +			} else +				bufferc(p, ASCII_NBRSP); +		} +		if (p->flags & TERMP_PREKEEP) +			p->flags |= TERMP_KEEP; +		if (p->flags & TERMP_NONOSPACE) +			p->flags |= TERMP_NOSPACE; +		else +			p->flags &= ~TERMP_NOSPACE; +		p->flags &= ~(TERMP_SENTENCE | TERMP_NONEWLINE); +		p->skipvsp = 0;  	} -	if (TERMP_PREKEEP & p->flags) -		p->flags |= TERMP_KEEP; - -	if ( ! (p->flags & TERMP_NONOSPACE)) -		p->flags &= ~TERMP_NOSPACE; -	else -		p->flags |= TERMP_NOSPACE; - -	p->flags &= ~(TERMP_SENTENCE | TERMP_NONEWLINE); -	p->skipvsp = 0;  	while ('\0' != *word) {  		if ('\\' != *word) { @@ -479,12 +516,88 @@ term_word(struct termp *p, const char *word)  		case ESCAPE_FONTPREV:  			term_fontlast(p);  			continue; +		case ESCAPE_BREAK: +			bufferc(p, '\n'); +			continue;  		case ESCAPE_NOSPACE:  			if (p->flags & TERMP_BACKAFTER)  				p->flags &= ~TERMP_BACKAFTER;  			else if (*word == '\0')  				p->flags |= (TERMP_NOSPACE | TERMP_NONEWLINE);  			continue; +		case ESCAPE_HORIZ: +			if (*seq == '|') { +				seq++; +				uc = -p->col; +			} else +				uc = 0; +			if (a2roffsu(seq, &su, SCALE_EM) == NULL) +				continue; +			uc += term_hen(p, &su); +			if (uc > 0) +				while (uc-- > 0) +					bufferc(p, ASCII_NBRSP); +			else if (p->col > (size_t)(-uc)) +				p->col += uc; +			else { +				uc += p->col; +				p->col = 0; +				if (p->tcol->offset > (size_t)(-uc)) { +					p->ti += uc; +					p->tcol->offset += uc; +				} else { +					p->ti -= p->tcol->offset; +					p->tcol->offset = 0; +				} +			} +			continue; +		case ESCAPE_HLINE: +			if ((cp = a2roffsu(seq, &su, SCALE_EM)) == NULL) +				continue; +			uc = term_hen(p, &su); +			if (uc <= 0) { +				if (p->tcol->rmargin <= p->tcol->offset) +					continue; +				lsz = p->tcol->rmargin - p->tcol->offset; +			} else +				lsz = uc; +			if (*cp == seq[-1]) +				uc = -1; +			else if (*cp == '\\') { +				seq = cp + 1; +				esc = mandoc_escape(&seq, &cp, &sz); +				switch (esc) { +				case ESCAPE_UNICODE: +					uc = mchars_num2uc(cp + 1, sz - 1); +					break; +				case ESCAPE_NUMBERED: +					uc = mchars_num2char(cp, sz); +					break; +				case ESCAPE_SPECIAL: +					uc = mchars_spec2cp(cp, sz); +					break; +				default: +					uc = -1; +					break; +				} +			} else +				uc = *cp; +			if (uc < 0x20 || (uc > 0x7E && uc < 0xA0)) +				uc = '_'; +			if (p->enc == TERMENC_ASCII) { +				cp = ascii_uc2str(uc); +				csz = term_strlen(p, cp); +				ssz = strlen(cp); +			} else +				csz = (*p->width)(p, uc); +			while (lsz >= csz) { +				if (p->enc == TERMENC_ASCII) +					encode(p, cp, ssz); +				else +					encode1(p, uc); +				lsz -= csz; +			} +			continue;  		case ESCAPE_SKIPCHAR:  			p->flags |= TERMP_BACKAFTER;  			continue; @@ -504,10 +617,12 @@ term_word(struct termp *p, const char *word)  				}  			}  			/* Trim trailing backspace/blank pair. */ -			if (p->col > 2 && -			    (p->buf[p->col - 1] == ' ' || -			     p->buf[p->col - 1] == '\t')) -				p->col -= 2; +			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; @@ -532,25 +647,28 @@ term_word(struct termp *p, const char *word)  }  static void -adjbuf(struct termp *p, size_t sz) +adjbuf(struct termp_col *c, size_t sz)  { - -	if (0 == p->maxcols) -		p->maxcols = 1024; -	while (sz >= p->maxcols) -		p->maxcols <<= 2; - -	p->buf = mandoc_reallocarray(p->buf, p->maxcols, sizeof(int)); +	if (c->maxcols == 0) +		c->maxcols = 1024; +	while (c->maxcols <= sz) +		c->maxcols <<= 2; +	c->buf = mandoc_reallocarray(c->buf, c->maxcols, sizeof(*c->buf));  }  static void  bufferc(struct termp *p, char c)  { - -	if (p->col + 1 >= p->maxcols) -		adjbuf(p, p->col + 1); - -	p->buf[p->col++] = c; +	if (p->flags & TERMP_NOBUF) { +		(*p->letter)(p, c); +		return; +	} +	if (p->col + 1 >= p->tcol->maxcols) +		adjbuf(p->tcol, p->col + 1); +	if (p->tcol->lastcol <= p->col || (c != ' ' && c != ASCII_NBRSP)) +		p->tcol->buf[p->col] = c; +	if (p->tcol->lastcol < ++p->col) +		p->tcol->lastcol = p->col;  }  /* @@ -563,31 +681,40 @@ encode1(struct termp *p, int c)  {  	enum termfont	  f; -	if (p->col + 7 >= p->maxcols) -		adjbuf(p, p->col + 7); +	if (p->flags & TERMP_NOBUF) { +		(*p->letter)(p, c); +		return; +	} + +	if (p->col + 7 >= p->tcol->maxcols) +		adjbuf(p->tcol, p->col + 7);  	f = (c == ASCII_HYPH || c > 127 || isgraph(c)) ?  	    p->fontq[p->fonti] : TERMFONT_NONE;  	if (p->flags & TERMP_BACKBEFORE) { -		if (p->buf[p->col - 1] == ' ' || p->buf[p->col - 1] == '\t') +		if (p->tcol->buf[p->col - 1] == ' ' || +		    p->tcol->buf[p->col - 1] == '\t')  			p->col--;  		else -			p->buf[p->col++] = 8; +			p->tcol->buf[p->col++] = '\b';  		p->flags &= ~TERMP_BACKBEFORE;  	} -	if (TERMFONT_UNDER == f || TERMFONT_BI == f) { -		p->buf[p->col++] = '_'; -		p->buf[p->col++] = 8; +	if (f == TERMFONT_UNDER || f == TERMFONT_BI) { +		p->tcol->buf[p->col++] = '_'; +		p->tcol->buf[p->col++] = '\b';  	} -	if (TERMFONT_BOLD == f || TERMFONT_BI == f) { -		if (ASCII_HYPH == c) -			p->buf[p->col++] = '-'; +	if (f == TERMFONT_BOLD || f == TERMFONT_BI) { +		if (c == ASCII_HYPH) +			p->tcol->buf[p->col++] = '-';  		else -			p->buf[p->col++] = c; -		p->buf[p->col++] = 8; +			p->tcol->buf[p->col++] = c; +		p->tcol->buf[p->col++] = '\b';  	} -	p->buf[p->col++] = c; +	if (p->tcol->lastcol <= p->col || (c != ' ' && c != ASCII_NBRSP)) +		p->tcol->buf[p->col] = c; +	if (p->tcol->lastcol < ++p->col) +		p->tcol->lastcol = p->col;  	if (p->flags & TERMP_BACKAFTER) {  		p->flags |= TERMP_BACKBEFORE;  		p->flags &= ~TERMP_BACKAFTER; @@ -599,15 +726,24 @@ encode(struct termp *p, const char *word, size_t sz)  {  	size_t		  i; -	if (p->col + 2 + (sz * 5) >= p->maxcols) -		adjbuf(p, p->col + 2 + (sz * 5)); +	if (p->flags & TERMP_NOBUF) { +		for (i = 0; i < sz; i++) +			(*p->letter)(p, word[i]); +		return; +	} + +	if (p->col + 2 + (sz * 5) >= p->tcol->maxcols) +		adjbuf(p->tcol, p->col + 2 + (sz * 5));  	for (i = 0; i < sz; i++) {  		if (ASCII_HYPH == word[i] ||  		    isgraph((unsigned char)word[i]))  			encode1(p, word[i]);  		else { -			p->buf[p->col++] = word[i]; +			if (p->tcol->lastcol <= p->col || +			    (word[i] != ' ' && word[i] != ASCII_NBRSP)) +				p->tcol->buf[p->col] = word[i]; +			p->col++;  			/*  			 * Postpone the effect of \z while handling @@ -621,6 +757,8 @@ encode(struct termp *p, const char *word, size_t sz)  			}  		}  	} +	if (p->tcol->lastcol < p->col) +		p->tcol->lastcol = p->col;  }  void @@ -644,7 +782,7 @@ term_setwidth(struct termp *p, const char *wstr)  		default:  			break;  		} -		if (a2roffsu(wstr, &su, SCALE_MAX)) +		if (a2roffsu(wstr, &su, SCALE_MAX) != NULL)  			width = term_hspan(p, &su);  		else  			iop = 0; @@ -833,7 +971,7 @@ term_vspan(const struct termp *p, const struct roffsu *su)  }  /* - * Convert a scaling width to basic units, rounding down. + * Convert a scaling width to basic units, rounding towards 0.   */  int  term_hspan(const struct termp *p, const struct roffsu *su) @@ -841,3 +979,17 @@ term_hspan(const struct termp *p, const struct roffsu *su)  	return (*p->hspan)(p, su);  } + +/* + * Convert a scaling width to basic units, rounding to closest. + */ +int +term_hen(const struct termp *p, const struct roffsu *su) +{ +	int bu; + +	if ((bu = (*p->hspan)(p, su)) >= 0) +		return (bu + 11) / 24; +	else +		return -((-bu + 11) / 24); +} | 
