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