Annotation of mandoc/term.c, Revision 1.168
1.168 ! schwarze 1: /* $Id: term.c,v 1.167 2010/08/20 01:02:07 schwarze Exp $ */
1.1 kristaps 2: /*
1.159 schwarze 3: * Copyright (c) 2008, 2009, 2010 Kristaps Dzonsons <kristaps@bsd.lv>
4: * Copyright (c) 2010 Ingo Schwarze <schwarze@openbsd.org>
1.1 kristaps 5: *
6: * Permission to use, copy, modify, and distribute this software for any
1.74 kristaps 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.74 kristaps 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.128 kristaps 18: #ifdef HAVE_CONFIG_H
19: #include "config.h"
20: #endif
21:
1.126 kristaps 22: #include <sys/types.h>
23:
1.1 kristaps 24: #include <assert.h>
1.122 kristaps 25: #include <ctype.h>
1.141 kristaps 26: #include <stdint.h>
1.22 kristaps 27: #include <stdio.h>
1.1 kristaps 28: #include <stdlib.h>
29: #include <string.h>
30:
1.137 kristaps 31: #include "mandoc.h"
1.101 kristaps 32: #include "chars.h"
1.107 kristaps 33: #include "out.h"
1.71 kristaps 34: #include "term.h"
1.105 kristaps 35: #include "main.h"
1.1 kristaps 36:
1.163 kristaps 37: static void spec(struct termp *, enum roffdeco,
38: const char *, size_t);
1.125 kristaps 39: static void res(struct termp *, const char *, size_t);
40: static void bufferc(struct termp *, char);
41: static void adjbuf(struct termp *p, size_t);
42: static void encode(struct termp *, const char *, size_t);
1.11 kristaps 43:
44:
1.145 kristaps 45: void
1.71 kristaps 46: term_free(struct termp *p)
1.14 kristaps 47: {
48:
1.71 kristaps 49: if (p->buf)
50: free(p->buf);
1.102 kristaps 51: if (p->symtab)
1.101 kristaps 52: chars_free(p->symtab);
1.145 kristaps 53:
1.142 kristaps 54: free(p);
55: }
56:
57:
58: void
59: term_begin(struct termp *p, term_margin head,
60: term_margin foot, const void *arg)
61: {
62:
63: p->headf = head;
64: p->footf = foot;
65: p->argf = arg;
1.146 kristaps 66: (*p->begin)(p);
1.142 kristaps 67: }
68:
69:
70: void
71: term_end(struct termp *p)
72: {
73:
1.146 kristaps 74: (*p->end)(p);
1.14 kristaps 75: }
76:
77:
1.145 kristaps 78: struct termp *
79: term_alloc(enum termenc enc)
1.14 kristaps 80: {
1.141 kristaps 81: struct termp *p;
1.14 kristaps 82:
1.117 kristaps 83: p = calloc(1, sizeof(struct termp));
84: if (NULL == p) {
1.120 kristaps 85: perror(NULL);
1.167 schwarze 86: exit(MANDOCLEVEL_SYSERR);
1.117 kristaps 87: }
1.141 kristaps 88:
1.71 kristaps 89: p->enc = enc;
90: return(p);
1.14 kristaps 91: }
92:
93:
1.71 kristaps 94: /*
95: * Flush a line of text. A "line" is loosely defined as being something
96: * that should be followed by a newline, regardless of whether it's
97: * broken apart by newlines getting there. A line can also be a
1.130 kristaps 98: * fragment of a columnar list (`Bl -tag' or `Bl -column'), which does
99: * not have a trailing newline.
1.71 kristaps 100: *
1.130 kristaps 101: * The following flags may be specified:
1.71 kristaps 102: *
103: * - TERMP_NOLPAD: when beginning to write the line, don't left-pad the
104: * offset value. This is useful when doing columnar lists where the
105: * prior column has right-padded.
106: *
107: * - TERMP_NOBREAK: this is the most important and is used when making
108: * columns. In short: don't print a newline and instead pad to the
109: * right margin. Used in conjunction with TERMP_NOLPAD.
110: *
1.91 kristaps 111: * - TERMP_TWOSPACE: when padding, make sure there are at least two
112: * space characters of padding. Otherwise, rather break the line.
113: *
1.84 kristaps 114: * - TERMP_DANGLE: don't newline when TERMP_NOBREAK is specified and
115: * the line is overrun, and don't pad-right if it's underrun.
116: *
117: * - TERMP_HANG: like TERMP_DANGLE, but doesn't newline when
118: * overruning, instead save the position and continue at that point
119: * when the next invocation.
1.71 kristaps 120: *
121: * In-line line breaking:
122: *
123: * If TERMP_NOBREAK is specified and the line overruns the right
124: * margin, it will break and pad-right to the right margin after
125: * writing. If maxrmargin is violated, it will break and continue
1.114 kristaps 126: * writing from the right-margin, which will lead to the above scenario
127: * upon exit. Otherwise, the line will break at the right margin.
1.71 kristaps 128: */
129: void
130: term_flushln(struct termp *p)
1.53 kristaps 131: {
1.114 kristaps 132: int i; /* current input position in p->buf */
133: size_t vis; /* current visual position on output */
134: size_t vbl; /* number of blanks to prepend to output */
1.136 schwarze 135: size_t vend; /* end of word visual position on output */
1.114 kristaps 136: size_t bp; /* visual right border position */
1.152 kristaps 137: int j; /* temporary loop index for p->buf */
138: int jhy; /* last hyph before overflow w/r/t j */
139: size_t maxvis; /* output position of visible boundary */
140: size_t mmax; /* used in calculating bp */
1.53 kristaps 141:
1.71 kristaps 142: /*
143: * First, establish the maximum columns of "visible" content.
144: * This is usually the difference between the right-margin and
145: * an indentation, but can be, for tagged lists or columns, a
1.115 kristaps 146: * small set of values.
1.71 kristaps 147: */
1.53 kristaps 148:
1.71 kristaps 149: assert(p->offset < p->rmargin);
1.92 kristaps 150:
1.129 kristaps 151: maxvis = (int)(p->rmargin - p->offset) - p->overstep < 0 ?
1.119 kristaps 152: /* LINTED */
1.129 kristaps 153: 0 : p->rmargin - p->offset - p->overstep;
154: mmax = (int)(p->maxrmargin - p->offset) - p->overstep < 0 ?
1.119 kristaps 155: /* LINTED */
1.129 kristaps 156: 0 : p->maxrmargin - p->offset - p->overstep;
1.92 kristaps 157:
1.71 kristaps 158: bp = TERMP_NOBREAK & p->flags ? mmax : maxvis;
1.115 kristaps 159:
1.136 schwarze 160: /*
161: * Indent the first line of a paragraph.
162: */
163: vbl = p->flags & TERMP_NOLPAD ? 0 : p->offset;
164:
1.154 kristaps 165: vis = vend = i = 0;
1.115 kristaps 166:
1.136 schwarze 167: while (i < (int)p->col) {
1.71 kristaps 168: /*
1.154 kristaps 169: * Handle literal tab characters: collapse all
170: * subsequent tabs into a single huge set of spaces.
1.138 schwarze 171: */
172: for (j = i; j < (int)p->col; j++) {
173: if ('\t' != p->buf[j])
174: break;
1.154 kristaps 175: vend = (vis / p->tabwidth + 1) * p->tabwidth;
1.138 schwarze 176: vbl += vend - vis;
177: vis = vend;
178: }
179:
180: /*
1.71 kristaps 181: * Count up visible word characters. Control sequences
182: * (starting with the CSI) aren't counted. A space
183: * generates a non-printing word, which is valid (the
184: * space is printed according to regular spacing rules).
185: */
186:
187: /* LINTED */
1.140 kristaps 188: for (jhy = 0; j < (int)p->col; j++) {
1.138 schwarze 189: if ((j && ' ' == p->buf[j]) || '\t' == p->buf[j])
1.71 kristaps 190: break;
1.154 kristaps 191:
192: /* Back over the the last printed character. */
193: if (8 == p->buf[j]) {
1.153 kristaps 194: assert(j);
195: vend -= (*p->width)(p, p->buf[j - 1]);
1.154 kristaps 196: continue;
1.153 kristaps 197: }
1.154 kristaps 198:
199: /* Regular word. */
200: /* Break at the hyphen point if we overrun. */
201: if (vend > vis && vend < bp &&
202: ASCII_HYPH == p->buf[j])
203: jhy = j;
204:
205: vend += (*p->width)(p, p->buf[j]);
1.71 kristaps 206: }
1.53 kristaps 207:
1.71 kristaps 208: /*
1.81 kristaps 209: * Find out whether we would exceed the right margin.
1.136 schwarze 210: * If so, break to the next line.
1.81 kristaps 211: */
1.140 kristaps 212: if (vend > bp && 0 == jhy && vis > 0) {
1.136 schwarze 213: vend -= vis;
1.146 kristaps 214: (*p->endline)(p);
1.81 kristaps 215: if (TERMP_NOBREAK & p->flags) {
1.139 schwarze 216: p->viscol = p->rmargin;
1.146 kristaps 217: (*p->advance)(p, p->rmargin);
1.136 schwarze 218: vend += p->rmargin - p->offset;
1.81 kristaps 219: } else {
1.139 schwarze 220: p->viscol = 0;
1.136 schwarze 221: vbl = p->offset;
1.81 kristaps 222: }
1.130 kristaps 223:
1.129 kristaps 224: /* Remove the p->overstep width. */
1.130 kristaps 225:
1.112 kristaps 226: bp += (int)/* LINTED */
1.129 kristaps 227: p->overstep;
228: p->overstep = 0;
1.71 kristaps 229: }
1.53 kristaps 230:
1.138 schwarze 231: /*
232: * Skip leading tabs, they were handled above.
233: */
234: while (i < (int)p->col && '\t' == p->buf[i])
235: i++;
236:
1.130 kristaps 237: /* Write out the [remaining] word. */
1.136 schwarze 238: for ( ; i < (int)p->col; i++) {
1.140 kristaps 239: if (vend > bp && jhy > 0 && i > jhy)
240: break;
1.138 schwarze 241: if ('\t' == p->buf[i])
242: break;
1.136 schwarze 243: if (' ' == p->buf[i]) {
1.164 kristaps 244: j = i;
245: while (' ' == p->buf[i])
1.136 schwarze 246: i++;
1.164 kristaps 247: vbl += (i - j) * (*p->width)(p, ' ');
1.71 kristaps 248: break;
1.136 schwarze 249: }
250: if (ASCII_NBRSP == p->buf[i]) {
1.153 kristaps 251: vbl += (*p->width)(p, ' ');
1.136 schwarze 252: continue;
253: }
1.130 kristaps 254:
1.136 schwarze 255: /*
256: * Now we definitely know there will be
257: * printable characters to output,
258: * so write preceding white space now.
259: */
260: if (vbl) {
1.146 kristaps 261: (*p->advance)(p, vbl);
1.139 schwarze 262: p->viscol += vbl;
1.136 schwarze 263: vbl = 0;
264: }
1.140 kristaps 265:
1.153 kristaps 266: if (ASCII_HYPH == p->buf[i]) {
1.146 kristaps 267: (*p->letter)(p, '-');
1.153 kristaps 268: p->viscol += (*p->width)(p, '-');
269: } else {
1.146 kristaps 270: (*p->letter)(p, p->buf[i]);
1.153 kristaps 271: p->viscol += (*p->width)(p, p->buf[i]);
272: }
1.136 schwarze 273: }
274: vend += vbl;
275: vis = vend;
1.71 kristaps 276: }
1.168 ! schwarze 277:
! 278: /*
! 279: * If there was trailing white space, it was not printed;
! 280: * so reset the cursor position accordingly.
! 281: */
! 282: vis -= vbl;
1.111 kristaps 283:
1.91 kristaps 284: p->col = 0;
1.129 kristaps 285: p->overstep = 0;
1.15 kristaps 286:
1.91 kristaps 287: if ( ! (TERMP_NOBREAK & p->flags)) {
1.139 schwarze 288: p->viscol = 0;
1.146 kristaps 289: (*p->endline)(p);
1.15 kristaps 290: return;
1.71 kristaps 291: }
1.15 kristaps 292:
1.91 kristaps 293: if (TERMP_HANG & p->flags) {
294: /* We need one blank after the tag. */
1.129 kristaps 295: p->overstep = /* LINTED */
1.153 kristaps 296: vis - maxvis + (*p->width)(p, ' ');
1.91 kristaps 297:
298: /*
299: * Behave exactly the same way as groff:
1.92 kristaps 300: * If we have overstepped the margin, temporarily move
301: * it to the right and flag the rest of the line to be
302: * shorter.
1.91 kristaps 303: * If we landed right at the margin, be happy.
1.92 kristaps 304: * If we are one step before the margin, temporarily
305: * move it one step LEFT and flag the rest of the line
306: * to be longer.
1.91 kristaps 307: */
1.129 kristaps 308: if (p->overstep >= -1) {
309: assert((int)maxvis + p->overstep >= 0);
1.92 kristaps 310: /* LINTED */
1.129 kristaps 311: maxvis += p->overstep;
1.92 kristaps 312: } else
1.129 kristaps 313: p->overstep = 0;
1.91 kristaps 314:
315: } else if (TERMP_DANGLE & p->flags)
316: return;
1.15 kristaps 317:
1.92 kristaps 318: /* Right-pad. */
319: if (maxvis > vis + /* LINTED */
1.154 kristaps 320: ((TERMP_TWOSPACE & p->flags) ?
321: (*p->width)(p, ' ') : 0)) {
1.139 schwarze 322: p->viscol += maxvis - vis;
1.146 kristaps 323: (*p->advance)(p, maxvis - vis);
1.142 kristaps 324: vis += (maxvis - vis);
1.139 schwarze 325: } else { /* ...or newline break. */
1.146 kristaps 326: (*p->endline)(p);
1.139 schwarze 327: p->viscol = p->rmargin;
1.146 kristaps 328: (*p->advance)(p, p->rmargin);
1.91 kristaps 329: }
1.15 kristaps 330: }
331:
332:
1.71 kristaps 333: /*
334: * A newline only breaks an existing line; it won't assert vertical
335: * space. All data in the output buffer is flushed prior to the newline
336: * assertion.
337: */
338: void
339: term_newln(struct termp *p)
1.15 kristaps 340: {
341:
1.71 kristaps 342: p->flags |= TERMP_NOSPACE;
1.139 schwarze 343: if (0 == p->col && 0 == p->viscol) {
1.71 kristaps 344: p->flags &= ~TERMP_NOLPAD;
1.15 kristaps 345: return;
1.16 kristaps 346: }
1.71 kristaps 347: term_flushln(p);
348: p->flags &= ~TERMP_NOLPAD;
1.16 kristaps 349: }
350:
351:
1.71 kristaps 352: /*
353: * Asserts a vertical space (a full, empty line-break between lines).
354: * Note that if used twice, this will cause two blank spaces and so on.
355: * All data in the output buffer is flushed prior to the newline
356: * assertion.
357: */
358: void
359: term_vspace(struct termp *p)
1.16 kristaps 360: {
361:
1.62 kristaps 362: term_newln(p);
1.139 schwarze 363: p->viscol = 0;
1.146 kristaps 364: (*p->endline)(p);
1.16 kristaps 365: }
366:
367:
1.71 kristaps 368: static void
1.163 kristaps 369: spec(struct termp *p, enum roffdeco d, const char *word, size_t len)
1.17 kristaps 370: {
1.71 kristaps 371: const char *rhs;
372: size_t sz;
1.17 kristaps 373:
1.161 kristaps 374: rhs = chars_spec2str(p->symtab, word, len, &sz);
1.125 kristaps 375: if (rhs)
376: encode(p, rhs, sz);
1.163 kristaps 377: else if (DECO_SSPECIAL == d)
378: encode(p, word, len);
1.94 kristaps 379: }
380:
381:
382: static void
1.125 kristaps 383: res(struct termp *p, const char *word, size_t len)
1.94 kristaps 384: {
385: const char *rhs;
386: size_t sz;
387:
1.161 kristaps 388: rhs = chars_res2str(p->symtab, word, len, &sz);
1.125 kristaps 389: if (rhs)
390: encode(p, rhs, sz);
391: }
392:
393:
394: void
395: term_fontlast(struct termp *p)
396: {
397: enum termfont f;
398:
399: f = p->fontl;
400: p->fontl = p->fontq[p->fonti];
401: p->fontq[p->fonti] = f;
402: }
403:
404:
405: void
406: term_fontrepl(struct termp *p, enum termfont f)
407: {
408:
409: p->fontl = p->fontq[p->fonti];
410: p->fontq[p->fonti] = f;
411: }
412:
413:
414: void
415: term_fontpush(struct termp *p, enum termfont f)
416: {
417:
418: assert(p->fonti + 1 < 10);
419: p->fontl = p->fontq[p->fonti];
420: p->fontq[++p->fonti] = f;
421: }
422:
423:
424: const void *
425: term_fontq(struct termp *p)
426: {
427:
428: return(&p->fontq[p->fonti]);
429: }
430:
431:
432: enum termfont
433: term_fonttop(struct termp *p)
434: {
435:
436: return(p->fontq[p->fonti]);
437: }
438:
439:
440: void
441: term_fontpopq(struct termp *p, const void *key)
442: {
443:
444: while (p->fonti >= 0 && key != &p->fontq[p->fonti])
445: p->fonti--;
446: assert(p->fonti >= 0);
447: }
1.94 kristaps 448:
1.125 kristaps 449:
450: void
451: term_fontpop(struct termp *p)
452: {
453:
454: assert(p->fonti);
455: p->fonti--;
1.17 kristaps 456: }
457:
458:
1.71 kristaps 459: /*
460: * Handle pwords, partial words, which may be either a single word or a
461: * phrase that cannot be broken down (such as a literal string). This
462: * handles word styling.
463: */
1.86 kristaps 464: void
465: term_word(struct termp *p, const char *word)
1.65 kristaps 466: {
1.124 kristaps 467: const char *sv, *seq;
1.125 kristaps 468: int sz;
1.124 kristaps 469: size_t ssz;
470: enum roffdeco deco;
1.71 kristaps 471:
1.100 kristaps 472: sv = word;
473:
1.123 kristaps 474: if (word[0] && '\0' == word[1])
1.100 kristaps 475: switch (word[0]) {
476: case('.'):
477: /* FALLTHROUGH */
478: case(','):
479: /* FALLTHROUGH */
480: case(';'):
481: /* FALLTHROUGH */
482: case(':'):
483: /* FALLTHROUGH */
484: case('?'):
485: /* FALLTHROUGH */
486: case('!'):
487: /* FALLTHROUGH */
488: case(')'):
489: /* FALLTHROUGH */
490: case(']'):
491: if ( ! (TERMP_IGNDELIM & p->flags))
492: p->flags |= TERMP_NOSPACE;
493: break;
494: default:
495: break;
496: }
1.65 kristaps 497:
1.133 kristaps 498: if ( ! (TERMP_NOSPACE & p->flags)) {
1.151 schwarze 499: if ( ! (TERMP_KEEP & p->flags)) {
500: if (TERMP_PREKEEP & p->flags)
501: p->flags |= TERMP_KEEP;
1.133 kristaps 502: bufferc(p, ' ');
1.151 schwarze 503: if (TERMP_SENTENCE & p->flags)
504: bufferc(p, ' ');
505: } else
506: bufferc(p, ASCII_NBRSP);
1.133 kristaps 507: }
1.65 kristaps 508:
1.71 kristaps 509: if ( ! (p->flags & TERMP_NONOSPACE))
510: p->flags &= ~TERMP_NOSPACE;
1.166 kristaps 511: else
512: p->flags |= TERMP_NOSPACE;
1.133 kristaps 513:
514: p->flags &= ~TERMP_SENTENCE;
1.65 kristaps 515:
1.162 kristaps 516: while (*word) {
517: if ((ssz = strcspn(word, "\\")) > 0)
518: encode(p, word, ssz);
1.124 kristaps 519:
1.162 kristaps 520: word += ssz;
521: if ('\\' != *word)
1.124 kristaps 522: continue;
523:
524: seq = ++word;
525: sz = a2roffdeco(&deco, &seq, &ssz);
526:
527: switch (deco) {
528: case (DECO_RESERVED):
1.125 kristaps 529: res(p, seq, ssz);
1.124 kristaps 530: break;
531: case (DECO_SPECIAL):
1.163 kristaps 532: /* FALLTHROUGH */
533: case (DECO_SSPECIAL):
534: spec(p, deco, seq, ssz);
1.124 kristaps 535: break;
536: case (DECO_BOLD):
1.125 kristaps 537: term_fontrepl(p, TERMFONT_BOLD);
1.124 kristaps 538: break;
539: case (DECO_ITALIC):
1.125 kristaps 540: term_fontrepl(p, TERMFONT_UNDER);
1.124 kristaps 541: break;
542: case (DECO_ROMAN):
1.125 kristaps 543: term_fontrepl(p, TERMFONT_NONE);
1.124 kristaps 544: break;
545: case (DECO_PREVIOUS):
1.125 kristaps 546: term_fontlast(p);
1.124 kristaps 547: break;
548: default:
549: break;
550: }
1.127 kristaps 551:
1.124 kristaps 552: word += sz;
1.127 kristaps 553: if (DECO_NOSPACE == deco && '\0' == *word)
554: p->flags |= TERMP_NOSPACE;
1.124 kristaps 555: }
1.65 kristaps 556:
1.131 kristaps 557: /*
558: * Note that we don't process the pipe: the parser sees it as
559: * punctuation, but we don't in terms of typography.
560: */
1.162 kristaps 561: if (sv[0] && '\0' == sv[1])
1.100 kristaps 562: switch (sv[0]) {
563: case('('):
564: /* FALLTHROUGH */
565: case('['):
566: p->flags |= TERMP_NOSPACE;
567: break;
568: default:
569: break;
570: }
1.65 kristaps 571: }
572:
573:
1.71 kristaps 574: static void
1.125 kristaps 575: adjbuf(struct termp *p, size_t sz)
1.51 kristaps 576: {
577:
1.125 kristaps 578: if (0 == p->maxcols)
579: p->maxcols = 1024;
580: while (sz >= p->maxcols)
581: p->maxcols <<= 2;
582:
583: p->buf = realloc(p->buf, p->maxcols);
584: if (NULL == p->buf) {
585: perror(NULL);
1.167 schwarze 586: exit(MANDOCLEVEL_SYSERR);
1.71 kristaps 587: }
1.51 kristaps 588: }
589:
1.79 kristaps 590:
591: static void
1.125 kristaps 592: bufferc(struct termp *p, char c)
593: {
594:
595: if (p->col + 1 >= p->maxcols)
596: adjbuf(p, p->col + 1);
597:
1.126 kristaps 598: p->buf[(int)p->col++] = c;
1.125 kristaps 599: }
600:
601:
602: static void
603: encode(struct termp *p, const char *word, size_t sz)
604: {
605: enum termfont f;
606: int i;
607:
608: /*
609: * Encode and buffer a string of characters. If the current
610: * font mode is unset, buffer directly, else encode then buffer
611: * character by character.
612: */
613:
1.147 kristaps 614: if (TERMFONT_NONE == (f = term_fonttop(p))) {
1.165 kristaps 615: if (p->col + sz >= p->maxcols)
616: adjbuf(p, p->col + sz);
617: memcpy(&p->buf[(int)p->col], word, sz);
618: p->col += sz;
1.125 kristaps 619: return;
620: }
621:
1.165 kristaps 622: /* Pre-buffer, assuming worst-case. */
623:
624: if (p->col + 1 + (sz * 3) >= p->maxcols)
625: adjbuf(p, p->col + 1 + (sz * 3));
626:
1.125 kristaps 627: for (i = 0; i < (int)sz; i++) {
628: if ( ! isgraph((u_char)word[i])) {
1.165 kristaps 629: p->buf[(int)p->col++] = word[i];
1.125 kristaps 630: continue;
1.79 kristaps 631: }
1.125 kristaps 632:
633: if (TERMFONT_UNDER == f)
1.165 kristaps 634: p->buf[(int)p->col++] = '_';
1.125 kristaps 635: else
1.165 kristaps 636: p->buf[(int)p->col++] = word[i];
1.125 kristaps 637:
1.165 kristaps 638: p->buf[(int)p->col++] = 8;
639: p->buf[(int)p->col++] = word[i];
1.79 kristaps 640: }
641: }
1.106 kristaps 642:
643:
1.107 kristaps 644: size_t
1.149 kristaps 645: term_len(const struct termp *p, size_t sz)
646: {
647:
648: return((*p->width)(p, ' ') * sz);
649: }
650:
651:
652: size_t
653: term_strlen(const struct termp *p, const char *cp)
654: {
655: size_t sz;
656:
657: for (sz = 0; *cp; cp++)
658: sz += (*p->width)(p, *cp);
659:
660: return(sz);
661: }
662:
663:
1.157 kristaps 664: /* ARGSUSED */
1.149 kristaps 665: size_t
666: term_vspan(const struct termp *p, const struct roffsu *su)
1.106 kristaps 667: {
668: double r;
669:
1.107 kristaps 670: switch (su->unit) {
1.106 kristaps 671: case (SCALE_CM):
1.107 kristaps 672: r = su->scale * 2;
1.106 kristaps 673: break;
674: case (SCALE_IN):
1.107 kristaps 675: r = su->scale * 6;
1.106 kristaps 676: break;
677: case (SCALE_PC):
1.107 kristaps 678: r = su->scale;
1.106 kristaps 679: break;
680: case (SCALE_PT):
1.107 kristaps 681: r = su->scale / 8;
1.106 kristaps 682: break;
683: case (SCALE_MM):
1.107 kristaps 684: r = su->scale / 1000;
1.106 kristaps 685: break;
686: case (SCALE_VS):
1.107 kristaps 687: r = su->scale;
1.106 kristaps 688: break;
689: default:
1.107 kristaps 690: r = su->scale - 1;
1.106 kristaps 691: break;
692: }
693:
694: if (r < 0.0)
695: r = 0.0;
1.107 kristaps 696: return(/* LINTED */(size_t)
1.106 kristaps 697: r);
698: }
699:
700:
1.107 kristaps 701: size_t
1.149 kristaps 702: term_hspan(const struct termp *p, const struct roffsu *su)
1.106 kristaps 703: {
1.156 kristaps 704: double v;
1.108 kristaps 705:
1.156 kristaps 706: v = ((*p->hspan)(p, su));
707: if (v < 0.0)
708: v = 0.0;
709: return((size_t) /* LINTED */
710: v);
1.106 kristaps 711: }
CVSweb