xref: /titanic_50/usr/src/cmd/mandoc/term.c (revision 95c635efb7c3b86efc493e0447eaec7aecca3f0f)
1*95c635efSGarrett D'Amore /*	$Id: term.c,v 1.201 2011/09/21 09:57:13 schwarze Exp $ */
2*95c635efSGarrett D'Amore /*
3*95c635efSGarrett D'Amore  * Copyright (c) 2008, 2009, 2010, 2011 Kristaps Dzonsons <kristaps@bsd.lv>
4*95c635efSGarrett D'Amore  * Copyright (c) 2010, 2011 Ingo Schwarze <schwarze@openbsd.org>
5*95c635efSGarrett D'Amore  *
6*95c635efSGarrett D'Amore  * Permission to use, copy, modify, and distribute this software for any
7*95c635efSGarrett D'Amore  * purpose with or without fee is hereby granted, provided that the above
8*95c635efSGarrett D'Amore  * copyright notice and this permission notice appear in all copies.
9*95c635efSGarrett D'Amore  *
10*95c635efSGarrett D'Amore  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
11*95c635efSGarrett D'Amore  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
12*95c635efSGarrett D'Amore  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
13*95c635efSGarrett D'Amore  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
14*95c635efSGarrett D'Amore  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
15*95c635efSGarrett D'Amore  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
16*95c635efSGarrett D'Amore  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
17*95c635efSGarrett D'Amore  */
18*95c635efSGarrett D'Amore #ifdef HAVE_CONFIG_H
19*95c635efSGarrett D'Amore #include "config.h"
20*95c635efSGarrett D'Amore #endif
21*95c635efSGarrett D'Amore 
22*95c635efSGarrett D'Amore #include <sys/types.h>
23*95c635efSGarrett D'Amore 
24*95c635efSGarrett D'Amore #include <assert.h>
25*95c635efSGarrett D'Amore #include <ctype.h>
26*95c635efSGarrett D'Amore #include <stdint.h>
27*95c635efSGarrett D'Amore #include <stdio.h>
28*95c635efSGarrett D'Amore #include <stdlib.h>
29*95c635efSGarrett D'Amore #include <string.h>
30*95c635efSGarrett D'Amore 
31*95c635efSGarrett D'Amore #include "mandoc.h"
32*95c635efSGarrett D'Amore #include "out.h"
33*95c635efSGarrett D'Amore #include "term.h"
34*95c635efSGarrett D'Amore #include "main.h"
35*95c635efSGarrett D'Amore 
36*95c635efSGarrett D'Amore static	void		 adjbuf(struct termp *p, int);
37*95c635efSGarrett D'Amore static	void		 bufferc(struct termp *, char);
38*95c635efSGarrett D'Amore static	void		 encode(struct termp *, const char *, size_t);
39*95c635efSGarrett D'Amore static	void		 encode1(struct termp *, int);
40*95c635efSGarrett D'Amore 
41*95c635efSGarrett D'Amore void
42*95c635efSGarrett D'Amore term_free(struct termp *p)
43*95c635efSGarrett D'Amore {
44*95c635efSGarrett D'Amore 
45*95c635efSGarrett D'Amore 	if (p->buf)
46*95c635efSGarrett D'Amore 		free(p->buf);
47*95c635efSGarrett D'Amore 	if (p->symtab)
48*95c635efSGarrett D'Amore 		mchars_free(p->symtab);
49*95c635efSGarrett D'Amore 
50*95c635efSGarrett D'Amore 	free(p);
51*95c635efSGarrett D'Amore }
52*95c635efSGarrett D'Amore 
53*95c635efSGarrett D'Amore 
54*95c635efSGarrett D'Amore void
55*95c635efSGarrett D'Amore term_begin(struct termp *p, term_margin head,
56*95c635efSGarrett D'Amore 		term_margin foot, const void *arg)
57*95c635efSGarrett D'Amore {
58*95c635efSGarrett D'Amore 
59*95c635efSGarrett D'Amore 	p->headf = head;
60*95c635efSGarrett D'Amore 	p->footf = foot;
61*95c635efSGarrett D'Amore 	p->argf = arg;
62*95c635efSGarrett D'Amore 	(*p->begin)(p);
63*95c635efSGarrett D'Amore }
64*95c635efSGarrett D'Amore 
65*95c635efSGarrett D'Amore 
66*95c635efSGarrett D'Amore void
67*95c635efSGarrett D'Amore term_end(struct termp *p)
68*95c635efSGarrett D'Amore {
69*95c635efSGarrett D'Amore 
70*95c635efSGarrett D'Amore 	(*p->end)(p);
71*95c635efSGarrett D'Amore }
72*95c635efSGarrett D'Amore 
73*95c635efSGarrett D'Amore /*
74*95c635efSGarrett D'Amore  * Flush a line of text.  A "line" is loosely defined as being something
75*95c635efSGarrett D'Amore  * that should be followed by a newline, regardless of whether it's
76*95c635efSGarrett D'Amore  * broken apart by newlines getting there.  A line can also be a
77*95c635efSGarrett D'Amore  * fragment of a columnar list (`Bl -tag' or `Bl -column'), which does
78*95c635efSGarrett D'Amore  * not have a trailing newline.
79*95c635efSGarrett D'Amore  *
80*95c635efSGarrett D'Amore  * The following flags may be specified:
81*95c635efSGarrett D'Amore  *
82*95c635efSGarrett D'Amore  *  - TERMP_NOBREAK: this is the most important and is used when making
83*95c635efSGarrett D'Amore  *    columns.  In short: don't print a newline and instead expect the
84*95c635efSGarrett D'Amore  *    next call to do the padding up to the start of the next column.
85*95c635efSGarrett D'Amore  *
86*95c635efSGarrett D'Amore  *  - TERMP_TWOSPACE: make sure there is room for at least two space
87*95c635efSGarrett D'Amore  *    characters of padding.  Otherwise, rather break the line.
88*95c635efSGarrett D'Amore  *
89*95c635efSGarrett D'Amore  *  - TERMP_DANGLE: don't newline when TERMP_NOBREAK is specified and
90*95c635efSGarrett D'Amore  *    the line is overrun, and don't pad-right if it's underrun.
91*95c635efSGarrett D'Amore  *
92*95c635efSGarrett D'Amore  *  - TERMP_HANG: like TERMP_DANGLE, but doesn't newline when
93*95c635efSGarrett D'Amore  *    overrunning, instead save the position and continue at that point
94*95c635efSGarrett D'Amore  *    when the next invocation.
95*95c635efSGarrett D'Amore  *
96*95c635efSGarrett D'Amore  *  In-line line breaking:
97*95c635efSGarrett D'Amore  *
98*95c635efSGarrett D'Amore  *  If TERMP_NOBREAK is specified and the line overruns the right
99*95c635efSGarrett D'Amore  *  margin, it will break and pad-right to the right margin after
100*95c635efSGarrett D'Amore  *  writing.  If maxrmargin is violated, it will break and continue
101*95c635efSGarrett D'Amore  *  writing from the right-margin, which will lead to the above scenario
102*95c635efSGarrett D'Amore  *  upon exit.  Otherwise, the line will break at the right margin.
103*95c635efSGarrett D'Amore  */
104*95c635efSGarrett D'Amore void
105*95c635efSGarrett D'Amore term_flushln(struct termp *p)
106*95c635efSGarrett D'Amore {
107*95c635efSGarrett D'Amore 	int		 i;     /* current input position in p->buf */
108*95c635efSGarrett D'Amore 	size_t		 vis;   /* current visual position on output */
109*95c635efSGarrett D'Amore 	size_t		 vbl;   /* number of blanks to prepend to output */
110*95c635efSGarrett D'Amore 	size_t		 vend;	/* end of word visual position on output */
111*95c635efSGarrett D'Amore 	size_t		 bp;    /* visual right border position */
112*95c635efSGarrett D'Amore 	size_t		 dv;    /* temporary for visual pos calculations */
113*95c635efSGarrett D'Amore 	int		 j;     /* temporary loop index for p->buf */
114*95c635efSGarrett D'Amore 	int		 jhy;	/* last hyph before overflow w/r/t j */
115*95c635efSGarrett D'Amore 	size_t		 maxvis; /* output position of visible boundary */
116*95c635efSGarrett D'Amore 	size_t		 mmax; /* used in calculating bp */
117*95c635efSGarrett D'Amore 
118*95c635efSGarrett D'Amore 	/*
119*95c635efSGarrett D'Amore 	 * First, establish the maximum columns of "visible" content.
120*95c635efSGarrett D'Amore 	 * This is usually the difference between the right-margin and
121*95c635efSGarrett D'Amore 	 * an indentation, but can be, for tagged lists or columns, a
122*95c635efSGarrett D'Amore 	 * small set of values.
123*95c635efSGarrett D'Amore 	 */
124*95c635efSGarrett D'Amore 	assert  (p->rmargin >= p->offset);
125*95c635efSGarrett D'Amore 	dv     = p->rmargin - p->offset;
126*95c635efSGarrett D'Amore 	maxvis = (int)dv > p->overstep ? dv - (size_t)p->overstep : 0;
127*95c635efSGarrett D'Amore 	dv     = p->maxrmargin - p->offset;
128*95c635efSGarrett D'Amore 	mmax   = (int)dv > p->overstep ? dv - (size_t)p->overstep : 0;
129*95c635efSGarrett D'Amore 
130*95c635efSGarrett D'Amore 	bp = TERMP_NOBREAK & p->flags ? mmax : maxvis;
131*95c635efSGarrett D'Amore 
132*95c635efSGarrett D'Amore 	/*
133*95c635efSGarrett D'Amore 	 * Calculate the required amount of padding.
134*95c635efSGarrett D'Amore 	 */
135*95c635efSGarrett D'Amore 	vbl = p->offset + p->overstep > p->viscol ?
136*95c635efSGarrett D'Amore 	      p->offset + p->overstep - p->viscol : 0;
137*95c635efSGarrett D'Amore 
138*95c635efSGarrett D'Amore 	vis = vend = 0;
139*95c635efSGarrett D'Amore 	i = 0;
140*95c635efSGarrett D'Amore 
141*95c635efSGarrett D'Amore 	while (i < p->col) {
142*95c635efSGarrett D'Amore 		/*
143*95c635efSGarrett D'Amore 		 * Handle literal tab characters: collapse all
144*95c635efSGarrett D'Amore 		 * subsequent tabs into a single huge set of spaces.
145*95c635efSGarrett D'Amore 		 */
146*95c635efSGarrett D'Amore 		while (i < p->col && '\t' == p->buf[i]) {
147*95c635efSGarrett D'Amore 			vend = (vis / p->tabwidth + 1) * p->tabwidth;
148*95c635efSGarrett D'Amore 			vbl += vend - vis;
149*95c635efSGarrett D'Amore 			vis = vend;
150*95c635efSGarrett D'Amore 			i++;
151*95c635efSGarrett D'Amore 		}
152*95c635efSGarrett D'Amore 
153*95c635efSGarrett D'Amore 		/*
154*95c635efSGarrett D'Amore 		 * Count up visible word characters.  Control sequences
155*95c635efSGarrett D'Amore 		 * (starting with the CSI) aren't counted.  A space
156*95c635efSGarrett D'Amore 		 * generates a non-printing word, which is valid (the
157*95c635efSGarrett D'Amore 		 * space is printed according to regular spacing rules).
158*95c635efSGarrett D'Amore 		 */
159*95c635efSGarrett D'Amore 
160*95c635efSGarrett D'Amore 		for (j = i, jhy = 0; j < p->col; j++) {
161*95c635efSGarrett D'Amore 			if ((j && ' ' == p->buf[j]) || '\t' == p->buf[j])
162*95c635efSGarrett D'Amore 				break;
163*95c635efSGarrett D'Amore 
164*95c635efSGarrett D'Amore 			/* Back over the the last printed character. */
165*95c635efSGarrett D'Amore 			if (8 == p->buf[j]) {
166*95c635efSGarrett D'Amore 				assert(j);
167*95c635efSGarrett D'Amore 				vend -= (*p->width)(p, p->buf[j - 1]);
168*95c635efSGarrett D'Amore 				continue;
169*95c635efSGarrett D'Amore 			}
170*95c635efSGarrett D'Amore 
171*95c635efSGarrett D'Amore 			/* Regular word. */
172*95c635efSGarrett D'Amore 			/* Break at the hyphen point if we overrun. */
173*95c635efSGarrett D'Amore 			if (vend > vis && vend < bp &&
174*95c635efSGarrett D'Amore 					ASCII_HYPH == p->buf[j])
175*95c635efSGarrett D'Amore 				jhy = j;
176*95c635efSGarrett D'Amore 
177*95c635efSGarrett D'Amore 			vend += (*p->width)(p, p->buf[j]);
178*95c635efSGarrett D'Amore 		}
179*95c635efSGarrett D'Amore 
180*95c635efSGarrett D'Amore 		/*
181*95c635efSGarrett D'Amore 		 * Find out whether we would exceed the right margin.
182*95c635efSGarrett D'Amore 		 * If so, break to the next line.
183*95c635efSGarrett D'Amore 		 */
184*95c635efSGarrett D'Amore 		if (vend > bp && 0 == jhy && vis > 0) {
185*95c635efSGarrett D'Amore 			vend -= vis;
186*95c635efSGarrett D'Amore 			(*p->endline)(p);
187*95c635efSGarrett D'Amore 			p->viscol = 0;
188*95c635efSGarrett D'Amore 			if (TERMP_NOBREAK & p->flags) {
189*95c635efSGarrett D'Amore 				vbl = p->rmargin;
190*95c635efSGarrett D'Amore 				vend += p->rmargin - p->offset;
191*95c635efSGarrett D'Amore 			} else
192*95c635efSGarrett D'Amore 				vbl = p->offset;
193*95c635efSGarrett D'Amore 
194*95c635efSGarrett D'Amore 			/* Remove the p->overstep width. */
195*95c635efSGarrett D'Amore 
196*95c635efSGarrett D'Amore 			bp += (size_t)p->overstep;
197*95c635efSGarrett D'Amore 			p->overstep = 0;
198*95c635efSGarrett D'Amore 		}
199*95c635efSGarrett D'Amore 
200*95c635efSGarrett D'Amore 		/* Write out the [remaining] word. */
201*95c635efSGarrett D'Amore 		for ( ; i < p->col; i++) {
202*95c635efSGarrett D'Amore 			if (vend > bp && jhy > 0 && i > jhy)
203*95c635efSGarrett D'Amore 				break;
204*95c635efSGarrett D'Amore 			if ('\t' == p->buf[i])
205*95c635efSGarrett D'Amore 				break;
206*95c635efSGarrett D'Amore 			if (' ' == p->buf[i]) {
207*95c635efSGarrett D'Amore 				j = i;
208*95c635efSGarrett D'Amore 				while (' ' == p->buf[i])
209*95c635efSGarrett D'Amore 					i++;
210*95c635efSGarrett D'Amore 				dv = (size_t)(i - j) * (*p->width)(p, ' ');
211*95c635efSGarrett D'Amore 				vbl += dv;
212*95c635efSGarrett D'Amore 				vend += dv;
213*95c635efSGarrett D'Amore 				break;
214*95c635efSGarrett D'Amore 			}
215*95c635efSGarrett D'Amore 			if (ASCII_NBRSP == p->buf[i]) {
216*95c635efSGarrett D'Amore 				vbl += (*p->width)(p, ' ');
217*95c635efSGarrett D'Amore 				continue;
218*95c635efSGarrett D'Amore 			}
219*95c635efSGarrett D'Amore 
220*95c635efSGarrett D'Amore 			/*
221*95c635efSGarrett D'Amore 			 * Now we definitely know there will be
222*95c635efSGarrett D'Amore 			 * printable characters to output,
223*95c635efSGarrett D'Amore 			 * so write preceding white space now.
224*95c635efSGarrett D'Amore 			 */
225*95c635efSGarrett D'Amore 			if (vbl) {
226*95c635efSGarrett D'Amore 				(*p->advance)(p, vbl);
227*95c635efSGarrett D'Amore 				p->viscol += vbl;
228*95c635efSGarrett D'Amore 				vbl = 0;
229*95c635efSGarrett D'Amore 			}
230*95c635efSGarrett D'Amore 
231*95c635efSGarrett D'Amore 			if (ASCII_HYPH == p->buf[i]) {
232*95c635efSGarrett D'Amore 				(*p->letter)(p, '-');
233*95c635efSGarrett D'Amore 				p->viscol += (*p->width)(p, '-');
234*95c635efSGarrett D'Amore 				continue;
235*95c635efSGarrett D'Amore 			}
236*95c635efSGarrett D'Amore 
237*95c635efSGarrett D'Amore 			(*p->letter)(p, p->buf[i]);
238*95c635efSGarrett D'Amore 			if (8 == p->buf[i])
239*95c635efSGarrett D'Amore 				p->viscol -= (*p->width)(p, p->buf[i-1]);
240*95c635efSGarrett D'Amore 			else
241*95c635efSGarrett D'Amore 				p->viscol += (*p->width)(p, p->buf[i]);
242*95c635efSGarrett D'Amore 		}
243*95c635efSGarrett D'Amore 		vis = vend;
244*95c635efSGarrett D'Amore 	}
245*95c635efSGarrett D'Amore 
246*95c635efSGarrett D'Amore 	/*
247*95c635efSGarrett D'Amore 	 * If there was trailing white space, it was not printed;
248*95c635efSGarrett D'Amore 	 * so reset the cursor position accordingly.
249*95c635efSGarrett D'Amore 	 */
250*95c635efSGarrett D'Amore 	if (vis)
251*95c635efSGarrett D'Amore 		vis -= vbl;
252*95c635efSGarrett D'Amore 
253*95c635efSGarrett D'Amore 	p->col = 0;
254*95c635efSGarrett D'Amore 	p->overstep = 0;
255*95c635efSGarrett D'Amore 
256*95c635efSGarrett D'Amore 	if ( ! (TERMP_NOBREAK & p->flags)) {
257*95c635efSGarrett D'Amore 		p->viscol = 0;
258*95c635efSGarrett D'Amore 		(*p->endline)(p);
259*95c635efSGarrett D'Amore 		return;
260*95c635efSGarrett D'Amore 	}
261*95c635efSGarrett D'Amore 
262*95c635efSGarrett D'Amore 	if (TERMP_HANG & p->flags) {
263*95c635efSGarrett D'Amore 		/* We need one blank after the tag. */
264*95c635efSGarrett D'Amore 		p->overstep = (int)(vis - maxvis + (*p->width)(p, ' '));
265*95c635efSGarrett D'Amore 
266*95c635efSGarrett D'Amore 		/*
267*95c635efSGarrett D'Amore 		 * Behave exactly the same way as groff:
268*95c635efSGarrett D'Amore 		 * If we have overstepped the margin, temporarily move
269*95c635efSGarrett D'Amore 		 * it to the right and flag the rest of the line to be
270*95c635efSGarrett D'Amore 		 * shorter.
271*95c635efSGarrett D'Amore 		 * If we landed right at the margin, be happy.
272*95c635efSGarrett D'Amore 		 * If we are one step before the margin, temporarily
273*95c635efSGarrett D'Amore 		 * move it one step LEFT and flag the rest of the line
274*95c635efSGarrett D'Amore 		 * to be longer.
275*95c635efSGarrett D'Amore 		 */
276*95c635efSGarrett D'Amore 		if (p->overstep < -1)
277*95c635efSGarrett D'Amore 			p->overstep = 0;
278*95c635efSGarrett D'Amore 		return;
279*95c635efSGarrett D'Amore 
280*95c635efSGarrett D'Amore 	} else if (TERMP_DANGLE & p->flags)
281*95c635efSGarrett D'Amore 		return;
282*95c635efSGarrett D'Amore 
283*95c635efSGarrett D'Amore 	/* If the column was overrun, break the line. */
284*95c635efSGarrett D'Amore 	if (maxvis <= vis +
285*95c635efSGarrett D'Amore 	    ((TERMP_TWOSPACE & p->flags) ? (*p->width)(p, ' ') : 0)) {
286*95c635efSGarrett D'Amore 		(*p->endline)(p);
287*95c635efSGarrett D'Amore 		p->viscol = 0;
288*95c635efSGarrett D'Amore 	}
289*95c635efSGarrett D'Amore }
290*95c635efSGarrett D'Amore 
291*95c635efSGarrett D'Amore 
292*95c635efSGarrett D'Amore /*
293*95c635efSGarrett D'Amore  * A newline only breaks an existing line; it won't assert vertical
294*95c635efSGarrett D'Amore  * space.  All data in the output buffer is flushed prior to the newline
295*95c635efSGarrett D'Amore  * assertion.
296*95c635efSGarrett D'Amore  */
297*95c635efSGarrett D'Amore void
298*95c635efSGarrett D'Amore term_newln(struct termp *p)
299*95c635efSGarrett D'Amore {
300*95c635efSGarrett D'Amore 
301*95c635efSGarrett D'Amore 	p->flags |= TERMP_NOSPACE;
302*95c635efSGarrett D'Amore 	if (p->col || p->viscol)
303*95c635efSGarrett D'Amore 		term_flushln(p);
304*95c635efSGarrett D'Amore }
305*95c635efSGarrett D'Amore 
306*95c635efSGarrett D'Amore 
307*95c635efSGarrett D'Amore /*
308*95c635efSGarrett D'Amore  * Asserts a vertical space (a full, empty line-break between lines).
309*95c635efSGarrett D'Amore  * Note that if used twice, this will cause two blank spaces and so on.
310*95c635efSGarrett D'Amore  * All data in the output buffer is flushed prior to the newline
311*95c635efSGarrett D'Amore  * assertion.
312*95c635efSGarrett D'Amore  */
313*95c635efSGarrett D'Amore void
314*95c635efSGarrett D'Amore term_vspace(struct termp *p)
315*95c635efSGarrett D'Amore {
316*95c635efSGarrett D'Amore 
317*95c635efSGarrett D'Amore 	term_newln(p);
318*95c635efSGarrett D'Amore 	p->viscol = 0;
319*95c635efSGarrett D'Amore 	(*p->endline)(p);
320*95c635efSGarrett D'Amore }
321*95c635efSGarrett D'Amore 
322*95c635efSGarrett D'Amore void
323*95c635efSGarrett D'Amore term_fontlast(struct termp *p)
324*95c635efSGarrett D'Amore {
325*95c635efSGarrett D'Amore 	enum termfont	 f;
326*95c635efSGarrett D'Amore 
327*95c635efSGarrett D'Amore 	f = p->fontl;
328*95c635efSGarrett D'Amore 	p->fontl = p->fontq[p->fonti];
329*95c635efSGarrett D'Amore 	p->fontq[p->fonti] = f;
330*95c635efSGarrett D'Amore }
331*95c635efSGarrett D'Amore 
332*95c635efSGarrett D'Amore 
333*95c635efSGarrett D'Amore void
334*95c635efSGarrett D'Amore term_fontrepl(struct termp *p, enum termfont f)
335*95c635efSGarrett D'Amore {
336*95c635efSGarrett D'Amore 
337*95c635efSGarrett D'Amore 	p->fontl = p->fontq[p->fonti];
338*95c635efSGarrett D'Amore 	p->fontq[p->fonti] = f;
339*95c635efSGarrett D'Amore }
340*95c635efSGarrett D'Amore 
341*95c635efSGarrett D'Amore 
342*95c635efSGarrett D'Amore void
343*95c635efSGarrett D'Amore term_fontpush(struct termp *p, enum termfont f)
344*95c635efSGarrett D'Amore {
345*95c635efSGarrett D'Amore 
346*95c635efSGarrett D'Amore 	assert(p->fonti + 1 < 10);
347*95c635efSGarrett D'Amore 	p->fontl = p->fontq[p->fonti];
348*95c635efSGarrett D'Amore 	p->fontq[++p->fonti] = f;
349*95c635efSGarrett D'Amore }
350*95c635efSGarrett D'Amore 
351*95c635efSGarrett D'Amore 
352*95c635efSGarrett D'Amore const void *
353*95c635efSGarrett D'Amore term_fontq(struct termp *p)
354*95c635efSGarrett D'Amore {
355*95c635efSGarrett D'Amore 
356*95c635efSGarrett D'Amore 	return(&p->fontq[p->fonti]);
357*95c635efSGarrett D'Amore }
358*95c635efSGarrett D'Amore 
359*95c635efSGarrett D'Amore 
360*95c635efSGarrett D'Amore enum termfont
361*95c635efSGarrett D'Amore term_fonttop(struct termp *p)
362*95c635efSGarrett D'Amore {
363*95c635efSGarrett D'Amore 
364*95c635efSGarrett D'Amore 	return(p->fontq[p->fonti]);
365*95c635efSGarrett D'Amore }
366*95c635efSGarrett D'Amore 
367*95c635efSGarrett D'Amore 
368*95c635efSGarrett D'Amore void
369*95c635efSGarrett D'Amore term_fontpopq(struct termp *p, const void *key)
370*95c635efSGarrett D'Amore {
371*95c635efSGarrett D'Amore 
372*95c635efSGarrett D'Amore 	while (p->fonti >= 0 && key != &p->fontq[p->fonti])
373*95c635efSGarrett D'Amore 		p->fonti--;
374*95c635efSGarrett D'Amore 	assert(p->fonti >= 0);
375*95c635efSGarrett D'Amore }
376*95c635efSGarrett D'Amore 
377*95c635efSGarrett D'Amore 
378*95c635efSGarrett D'Amore void
379*95c635efSGarrett D'Amore term_fontpop(struct termp *p)
380*95c635efSGarrett D'Amore {
381*95c635efSGarrett D'Amore 
382*95c635efSGarrett D'Amore 	assert(p->fonti);
383*95c635efSGarrett D'Amore 	p->fonti--;
384*95c635efSGarrett D'Amore }
385*95c635efSGarrett D'Amore 
386*95c635efSGarrett D'Amore /*
387*95c635efSGarrett D'Amore  * Handle pwords, partial words, which may be either a single word or a
388*95c635efSGarrett D'Amore  * phrase that cannot be broken down (such as a literal string).  This
389*95c635efSGarrett D'Amore  * handles word styling.
390*95c635efSGarrett D'Amore  */
391*95c635efSGarrett D'Amore void
392*95c635efSGarrett D'Amore term_word(struct termp *p, const char *word)
393*95c635efSGarrett D'Amore {
394*95c635efSGarrett D'Amore 	const char	*seq, *cp;
395*95c635efSGarrett D'Amore 	char		 c;
396*95c635efSGarrett D'Amore 	int		 sz, uc;
397*95c635efSGarrett D'Amore 	size_t		 ssz;
398*95c635efSGarrett D'Amore 	enum mandoc_esc	 esc;
399*95c635efSGarrett D'Amore 
400*95c635efSGarrett D'Amore 	if ( ! (TERMP_NOSPACE & p->flags)) {
401*95c635efSGarrett D'Amore 		if ( ! (TERMP_KEEP & p->flags)) {
402*95c635efSGarrett D'Amore 			if (TERMP_PREKEEP & p->flags)
403*95c635efSGarrett D'Amore 				p->flags |= TERMP_KEEP;
404*95c635efSGarrett D'Amore 			bufferc(p, ' ');
405*95c635efSGarrett D'Amore 			if (TERMP_SENTENCE & p->flags)
406*95c635efSGarrett D'Amore 				bufferc(p, ' ');
407*95c635efSGarrett D'Amore 		} else
408*95c635efSGarrett D'Amore 			bufferc(p, ASCII_NBRSP);
409*95c635efSGarrett D'Amore 	}
410*95c635efSGarrett D'Amore 
411*95c635efSGarrett D'Amore 	if ( ! (p->flags & TERMP_NONOSPACE))
412*95c635efSGarrett D'Amore 		p->flags &= ~TERMP_NOSPACE;
413*95c635efSGarrett D'Amore 	else
414*95c635efSGarrett D'Amore 		p->flags |= TERMP_NOSPACE;
415*95c635efSGarrett D'Amore 
416*95c635efSGarrett D'Amore 	p->flags &= ~(TERMP_SENTENCE | TERMP_IGNDELIM);
417*95c635efSGarrett D'Amore 
418*95c635efSGarrett D'Amore 	while ('\0' != *word) {
419*95c635efSGarrett D'Amore 		if ((ssz = strcspn(word, "\\")) > 0)
420*95c635efSGarrett D'Amore 			encode(p, word, ssz);
421*95c635efSGarrett D'Amore 
422*95c635efSGarrett D'Amore 		word += (int)ssz;
423*95c635efSGarrett D'Amore 		if ('\\' != *word)
424*95c635efSGarrett D'Amore 			continue;
425*95c635efSGarrett D'Amore 
426*95c635efSGarrett D'Amore 		word++;
427*95c635efSGarrett D'Amore 		esc = mandoc_escape(&word, &seq, &sz);
428*95c635efSGarrett D'Amore 		if (ESCAPE_ERROR == esc)
429*95c635efSGarrett D'Amore 			break;
430*95c635efSGarrett D'Amore 
431*95c635efSGarrett D'Amore 		if (TERMENC_ASCII != p->enc)
432*95c635efSGarrett D'Amore 			switch (esc) {
433*95c635efSGarrett D'Amore 			case (ESCAPE_UNICODE):
434*95c635efSGarrett D'Amore 				uc = mchars_num2uc(seq + 1, sz - 1);
435*95c635efSGarrett D'Amore 				if ('\0' == uc)
436*95c635efSGarrett D'Amore 					break;
437*95c635efSGarrett D'Amore 				encode1(p, uc);
438*95c635efSGarrett D'Amore 				continue;
439*95c635efSGarrett D'Amore 			case (ESCAPE_SPECIAL):
440*95c635efSGarrett D'Amore 				uc = mchars_spec2cp(p->symtab, seq, sz);
441*95c635efSGarrett D'Amore 				if (uc <= 0)
442*95c635efSGarrett D'Amore 					break;
443*95c635efSGarrett D'Amore 				encode1(p, uc);
444*95c635efSGarrett D'Amore 				continue;
445*95c635efSGarrett D'Amore 			default:
446*95c635efSGarrett D'Amore 				break;
447*95c635efSGarrett D'Amore 			}
448*95c635efSGarrett D'Amore 
449*95c635efSGarrett D'Amore 		switch (esc) {
450*95c635efSGarrett D'Amore 		case (ESCAPE_UNICODE):
451*95c635efSGarrett D'Amore 			encode1(p, '?');
452*95c635efSGarrett D'Amore 			break;
453*95c635efSGarrett D'Amore 		case (ESCAPE_NUMBERED):
454*95c635efSGarrett D'Amore 			c = mchars_num2char(seq, sz);
455*95c635efSGarrett D'Amore 			if ('\0' != c)
456*95c635efSGarrett D'Amore 				encode(p, &c, 1);
457*95c635efSGarrett D'Amore 			break;
458*95c635efSGarrett D'Amore 		case (ESCAPE_SPECIAL):
459*95c635efSGarrett D'Amore 			cp = mchars_spec2str(p->symtab, seq, sz, &ssz);
460*95c635efSGarrett D'Amore 			if (NULL != cp)
461*95c635efSGarrett D'Amore 				encode(p, cp, ssz);
462*95c635efSGarrett D'Amore 			else if (1 == ssz)
463*95c635efSGarrett D'Amore 				encode(p, seq, sz);
464*95c635efSGarrett D'Amore 			break;
465*95c635efSGarrett D'Amore 		case (ESCAPE_FONTBOLD):
466*95c635efSGarrett D'Amore 			term_fontrepl(p, TERMFONT_BOLD);
467*95c635efSGarrett D'Amore 			break;
468*95c635efSGarrett D'Amore 		case (ESCAPE_FONTITALIC):
469*95c635efSGarrett D'Amore 			term_fontrepl(p, TERMFONT_UNDER);
470*95c635efSGarrett D'Amore 			break;
471*95c635efSGarrett D'Amore 		case (ESCAPE_FONT):
472*95c635efSGarrett D'Amore 			/* FALLTHROUGH */
473*95c635efSGarrett D'Amore 		case (ESCAPE_FONTROMAN):
474*95c635efSGarrett D'Amore 			term_fontrepl(p, TERMFONT_NONE);
475*95c635efSGarrett D'Amore 			break;
476*95c635efSGarrett D'Amore 		case (ESCAPE_FONTPREV):
477*95c635efSGarrett D'Amore 			term_fontlast(p);
478*95c635efSGarrett D'Amore 			break;
479*95c635efSGarrett D'Amore 		case (ESCAPE_NOSPACE):
480*95c635efSGarrett D'Amore 			if ('\0' == *word)
481*95c635efSGarrett D'Amore 				p->flags |= TERMP_NOSPACE;
482*95c635efSGarrett D'Amore 			break;
483*95c635efSGarrett D'Amore 		default:
484*95c635efSGarrett D'Amore 			break;
485*95c635efSGarrett D'Amore 		}
486*95c635efSGarrett D'Amore 	}
487*95c635efSGarrett D'Amore }
488*95c635efSGarrett D'Amore 
489*95c635efSGarrett D'Amore static void
490*95c635efSGarrett D'Amore adjbuf(struct termp *p, int sz)
491*95c635efSGarrett D'Amore {
492*95c635efSGarrett D'Amore 
493*95c635efSGarrett D'Amore 	if (0 == p->maxcols)
494*95c635efSGarrett D'Amore 		p->maxcols = 1024;
495*95c635efSGarrett D'Amore 	while (sz >= p->maxcols)
496*95c635efSGarrett D'Amore 		p->maxcols <<= 2;
497*95c635efSGarrett D'Amore 
498*95c635efSGarrett D'Amore 	p->buf = mandoc_realloc
499*95c635efSGarrett D'Amore 		(p->buf, sizeof(int) * (size_t)p->maxcols);
500*95c635efSGarrett D'Amore }
501*95c635efSGarrett D'Amore 
502*95c635efSGarrett D'Amore static void
503*95c635efSGarrett D'Amore bufferc(struct termp *p, char c)
504*95c635efSGarrett D'Amore {
505*95c635efSGarrett D'Amore 
506*95c635efSGarrett D'Amore 	if (p->col + 1 >= p->maxcols)
507*95c635efSGarrett D'Amore 		adjbuf(p, p->col + 1);
508*95c635efSGarrett D'Amore 
509*95c635efSGarrett D'Amore 	p->buf[p->col++] = c;
510*95c635efSGarrett D'Amore }
511*95c635efSGarrett D'Amore 
512*95c635efSGarrett D'Amore /*
513*95c635efSGarrett D'Amore  * See encode().
514*95c635efSGarrett D'Amore  * Do this for a single (probably unicode) value.
515*95c635efSGarrett D'Amore  * Does not check for non-decorated glyphs.
516*95c635efSGarrett D'Amore  */
517*95c635efSGarrett D'Amore static void
518*95c635efSGarrett D'Amore encode1(struct termp *p, int c)
519*95c635efSGarrett D'Amore {
520*95c635efSGarrett D'Amore 	enum termfont	  f;
521*95c635efSGarrett D'Amore 
522*95c635efSGarrett D'Amore 	if (p->col + 4 >= p->maxcols)
523*95c635efSGarrett D'Amore 		adjbuf(p, p->col + 4);
524*95c635efSGarrett D'Amore 
525*95c635efSGarrett D'Amore 	f = term_fonttop(p);
526*95c635efSGarrett D'Amore 
527*95c635efSGarrett D'Amore 	if (TERMFONT_NONE == f) {
528*95c635efSGarrett D'Amore 		p->buf[p->col++] = c;
529*95c635efSGarrett D'Amore 		return;
530*95c635efSGarrett D'Amore 	} else if (TERMFONT_UNDER == f) {
531*95c635efSGarrett D'Amore 		p->buf[p->col++] = '_';
532*95c635efSGarrett D'Amore 	} else
533*95c635efSGarrett D'Amore 		p->buf[p->col++] = c;
534*95c635efSGarrett D'Amore 
535*95c635efSGarrett D'Amore 	p->buf[p->col++] = 8;
536*95c635efSGarrett D'Amore 	p->buf[p->col++] = c;
537*95c635efSGarrett D'Amore }
538*95c635efSGarrett D'Amore 
539*95c635efSGarrett D'Amore static void
540*95c635efSGarrett D'Amore encode(struct termp *p, const char *word, size_t sz)
541*95c635efSGarrett D'Amore {
542*95c635efSGarrett D'Amore 	enum termfont	  f;
543*95c635efSGarrett D'Amore 	int		  i, len;
544*95c635efSGarrett D'Amore 
545*95c635efSGarrett D'Amore 	/* LINTED */
546*95c635efSGarrett D'Amore 	len = sz;
547*95c635efSGarrett D'Amore 
548*95c635efSGarrett D'Amore 	/*
549*95c635efSGarrett D'Amore 	 * Encode and buffer a string of characters.  If the current
550*95c635efSGarrett D'Amore 	 * font mode is unset, buffer directly, else encode then buffer
551*95c635efSGarrett D'Amore 	 * character by character.
552*95c635efSGarrett D'Amore 	 */
553*95c635efSGarrett D'Amore 
554*95c635efSGarrett D'Amore 	if (TERMFONT_NONE == (f = term_fonttop(p))) {
555*95c635efSGarrett D'Amore 		if (p->col + len >= p->maxcols)
556*95c635efSGarrett D'Amore 			adjbuf(p, p->col + len);
557*95c635efSGarrett D'Amore 		for (i = 0; i < len; i++)
558*95c635efSGarrett D'Amore 			p->buf[p->col++] = word[i];
559*95c635efSGarrett D'Amore 		return;
560*95c635efSGarrett D'Amore 	}
561*95c635efSGarrett D'Amore 
562*95c635efSGarrett D'Amore 	/* Pre-buffer, assuming worst-case. */
563*95c635efSGarrett D'Amore 
564*95c635efSGarrett D'Amore 	if (p->col + 1 + (len * 3) >= p->maxcols)
565*95c635efSGarrett D'Amore 		adjbuf(p, p->col + 1 + (len * 3));
566*95c635efSGarrett D'Amore 
567*95c635efSGarrett D'Amore 	for (i = 0; i < len; i++) {
568*95c635efSGarrett D'Amore 		if (ASCII_HYPH != word[i] &&
569*95c635efSGarrett D'Amore 		    ! isgraph((unsigned char)word[i])) {
570*95c635efSGarrett D'Amore 			p->buf[p->col++] = word[i];
571*95c635efSGarrett D'Amore 			continue;
572*95c635efSGarrett D'Amore 		}
573*95c635efSGarrett D'Amore 
574*95c635efSGarrett D'Amore 		if (TERMFONT_UNDER == f)
575*95c635efSGarrett D'Amore 			p->buf[p->col++] = '_';
576*95c635efSGarrett D'Amore 		else if (ASCII_HYPH == word[i])
577*95c635efSGarrett D'Amore 			p->buf[p->col++] = '-';
578*95c635efSGarrett D'Amore 		else
579*95c635efSGarrett D'Amore 			p->buf[p->col++] = word[i];
580*95c635efSGarrett D'Amore 
581*95c635efSGarrett D'Amore 		p->buf[p->col++] = 8;
582*95c635efSGarrett D'Amore 		p->buf[p->col++] = word[i];
583*95c635efSGarrett D'Amore 	}
584*95c635efSGarrett D'Amore }
585*95c635efSGarrett D'Amore 
586*95c635efSGarrett D'Amore size_t
587*95c635efSGarrett D'Amore term_len(const struct termp *p, size_t sz)
588*95c635efSGarrett D'Amore {
589*95c635efSGarrett D'Amore 
590*95c635efSGarrett D'Amore 	return((*p->width)(p, ' ') * sz);
591*95c635efSGarrett D'Amore }
592*95c635efSGarrett D'Amore 
593*95c635efSGarrett D'Amore 
594*95c635efSGarrett D'Amore size_t
595*95c635efSGarrett D'Amore term_strlen(const struct termp *p, const char *cp)
596*95c635efSGarrett D'Amore {
597*95c635efSGarrett D'Amore 	size_t		 sz, rsz, i;
598*95c635efSGarrett D'Amore 	int		 ssz, c;
599*95c635efSGarrett D'Amore 	const char	*seq, *rhs;
600*95c635efSGarrett D'Amore 	enum mandoc_esc	 esc;
601*95c635efSGarrett D'Amore 	static const char rej[] = { '\\', ASCII_HYPH, ASCII_NBRSP, '\0' };
602*95c635efSGarrett D'Amore 
603*95c635efSGarrett D'Amore 	/*
604*95c635efSGarrett D'Amore 	 * Account for escaped sequences within string length
605*95c635efSGarrett D'Amore 	 * calculations.  This follows the logic in term_word() as we
606*95c635efSGarrett D'Amore 	 * must calculate the width of produced strings.
607*95c635efSGarrett D'Amore 	 */
608*95c635efSGarrett D'Amore 
609*95c635efSGarrett D'Amore 	sz = 0;
610*95c635efSGarrett D'Amore 	while ('\0' != *cp) {
611*95c635efSGarrett D'Amore 		rsz = strcspn(cp, rej);
612*95c635efSGarrett D'Amore 		for (i = 0; i < rsz; i++)
613*95c635efSGarrett D'Amore 			sz += (*p->width)(p, *cp++);
614*95c635efSGarrett D'Amore 
615*95c635efSGarrett D'Amore 		c = 0;
616*95c635efSGarrett D'Amore 		switch (*cp) {
617*95c635efSGarrett D'Amore 		case ('\\'):
618*95c635efSGarrett D'Amore 			cp++;
619*95c635efSGarrett D'Amore 			esc = mandoc_escape(&cp, &seq, &ssz);
620*95c635efSGarrett D'Amore 			if (ESCAPE_ERROR == esc)
621*95c635efSGarrett D'Amore 				return(sz);
622*95c635efSGarrett D'Amore 
623*95c635efSGarrett D'Amore 			if (TERMENC_ASCII != p->enc)
624*95c635efSGarrett D'Amore 				switch (esc) {
625*95c635efSGarrett D'Amore 				case (ESCAPE_UNICODE):
626*95c635efSGarrett D'Amore 					c = mchars_num2uc
627*95c635efSGarrett D'Amore 						(seq + 1, ssz - 1);
628*95c635efSGarrett D'Amore 					if ('\0' == c)
629*95c635efSGarrett D'Amore 						break;
630*95c635efSGarrett D'Amore 					sz += (*p->width)(p, c);
631*95c635efSGarrett D'Amore 					continue;
632*95c635efSGarrett D'Amore 				case (ESCAPE_SPECIAL):
633*95c635efSGarrett D'Amore 					c = mchars_spec2cp
634*95c635efSGarrett D'Amore 						(p->symtab, seq, ssz);
635*95c635efSGarrett D'Amore 					if (c <= 0)
636*95c635efSGarrett D'Amore 						break;
637*95c635efSGarrett D'Amore 					sz += (*p->width)(p, c);
638*95c635efSGarrett D'Amore 					continue;
639*95c635efSGarrett D'Amore 				default:
640*95c635efSGarrett D'Amore 					break;
641*95c635efSGarrett D'Amore 				}
642*95c635efSGarrett D'Amore 
643*95c635efSGarrett D'Amore 			rhs = NULL;
644*95c635efSGarrett D'Amore 
645*95c635efSGarrett D'Amore 			switch (esc) {
646*95c635efSGarrett D'Amore 			case (ESCAPE_UNICODE):
647*95c635efSGarrett D'Amore 				sz += (*p->width)(p, '?');
648*95c635efSGarrett D'Amore 				break;
649*95c635efSGarrett D'Amore 			case (ESCAPE_NUMBERED):
650*95c635efSGarrett D'Amore 				c = mchars_num2char(seq, ssz);
651*95c635efSGarrett D'Amore 				if ('\0' != c)
652*95c635efSGarrett D'Amore 					sz += (*p->width)(p, c);
653*95c635efSGarrett D'Amore 				break;
654*95c635efSGarrett D'Amore 			case (ESCAPE_SPECIAL):
655*95c635efSGarrett D'Amore 				rhs = mchars_spec2str
656*95c635efSGarrett D'Amore 					(p->symtab, seq, ssz, &rsz);
657*95c635efSGarrett D'Amore 
658*95c635efSGarrett D'Amore 				if (ssz != 1 || rhs)
659*95c635efSGarrett D'Amore 					break;
660*95c635efSGarrett D'Amore 
661*95c635efSGarrett D'Amore 				rhs = seq;
662*95c635efSGarrett D'Amore 				rsz = ssz;
663*95c635efSGarrett D'Amore 				break;
664*95c635efSGarrett D'Amore 			default:
665*95c635efSGarrett D'Amore 				break;
666*95c635efSGarrett D'Amore 			}
667*95c635efSGarrett D'Amore 
668*95c635efSGarrett D'Amore 			if (NULL == rhs)
669*95c635efSGarrett D'Amore 				break;
670*95c635efSGarrett D'Amore 
671*95c635efSGarrett D'Amore 			for (i = 0; i < rsz; i++)
672*95c635efSGarrett D'Amore 				sz += (*p->width)(p, *rhs++);
673*95c635efSGarrett D'Amore 			break;
674*95c635efSGarrett D'Amore 		case (ASCII_NBRSP):
675*95c635efSGarrett D'Amore 			sz += (*p->width)(p, ' ');
676*95c635efSGarrett D'Amore 			cp++;
677*95c635efSGarrett D'Amore 			break;
678*95c635efSGarrett D'Amore 		case (ASCII_HYPH):
679*95c635efSGarrett D'Amore 			sz += (*p->width)(p, '-');
680*95c635efSGarrett D'Amore 			cp++;
681*95c635efSGarrett D'Amore 			break;
682*95c635efSGarrett D'Amore 		default:
683*95c635efSGarrett D'Amore 			break;
684*95c635efSGarrett D'Amore 		}
685*95c635efSGarrett D'Amore 	}
686*95c635efSGarrett D'Amore 
687*95c635efSGarrett D'Amore 	return(sz);
688*95c635efSGarrett D'Amore }
689*95c635efSGarrett D'Amore 
690*95c635efSGarrett D'Amore /* ARGSUSED */
691*95c635efSGarrett D'Amore size_t
692*95c635efSGarrett D'Amore term_vspan(const struct termp *p, const struct roffsu *su)
693*95c635efSGarrett D'Amore {
694*95c635efSGarrett D'Amore 	double		 r;
695*95c635efSGarrett D'Amore 
696*95c635efSGarrett D'Amore 	switch (su->unit) {
697*95c635efSGarrett D'Amore 	case (SCALE_CM):
698*95c635efSGarrett D'Amore 		r = su->scale * 2;
699*95c635efSGarrett D'Amore 		break;
700*95c635efSGarrett D'Amore 	case (SCALE_IN):
701*95c635efSGarrett D'Amore 		r = su->scale * 6;
702*95c635efSGarrett D'Amore 		break;
703*95c635efSGarrett D'Amore 	case (SCALE_PC):
704*95c635efSGarrett D'Amore 		r = su->scale;
705*95c635efSGarrett D'Amore 		break;
706*95c635efSGarrett D'Amore 	case (SCALE_PT):
707*95c635efSGarrett D'Amore 		r = su->scale / 8;
708*95c635efSGarrett D'Amore 		break;
709*95c635efSGarrett D'Amore 	case (SCALE_MM):
710*95c635efSGarrett D'Amore 		r = su->scale / 1000;
711*95c635efSGarrett D'Amore 		break;
712*95c635efSGarrett D'Amore 	case (SCALE_VS):
713*95c635efSGarrett D'Amore 		r = su->scale;
714*95c635efSGarrett D'Amore 		break;
715*95c635efSGarrett D'Amore 	default:
716*95c635efSGarrett D'Amore 		r = su->scale - 1;
717*95c635efSGarrett D'Amore 		break;
718*95c635efSGarrett D'Amore 	}
719*95c635efSGarrett D'Amore 
720*95c635efSGarrett D'Amore 	if (r < 0.0)
721*95c635efSGarrett D'Amore 		r = 0.0;
722*95c635efSGarrett D'Amore 	return(/* LINTED */(size_t)
723*95c635efSGarrett D'Amore 			r);
724*95c635efSGarrett D'Amore }
725*95c635efSGarrett D'Amore 
726*95c635efSGarrett D'Amore size_t
727*95c635efSGarrett D'Amore term_hspan(const struct termp *p, const struct roffsu *su)
728*95c635efSGarrett D'Amore {
729*95c635efSGarrett D'Amore 	double		 v;
730*95c635efSGarrett D'Amore 
731*95c635efSGarrett D'Amore 	v = ((*p->hspan)(p, su));
732*95c635efSGarrett D'Amore 	if (v < 0.0)
733*95c635efSGarrett D'Amore 		v = 0.0;
734*95c635efSGarrett D'Amore 	return((size_t) /* LINTED */
735*95c635efSGarrett D'Amore 			v);
736*95c635efSGarrett D'Amore }
737