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