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