| Commit | Line | Data |
|---|---|---|
| 32c903ac | 1 | /* $Id: term.c,v 1.120 2009/10/31 06:10:58 kristaps Exp $ */ |
| 589e7c1d SW |
2 | /* |
| 3 | * Copyright (c) 2008, 2009 Kristaps Dzonsons <kristaps@kth.se> | |
| 4 | * | |
| 5 | * Permission to use, copy, modify, and distribute this software for any | |
| 6 | * purpose with or without fee is hereby granted, provided that the above | |
| 7 | * copyright notice and this permission notice appear in all copies. | |
| 8 | * | |
| 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. | |
| 16 | */ | |
| 17 | #include <assert.h> | |
| 589e7c1d SW |
18 | #include <stdio.h> |
| 19 | #include <stdlib.h> | |
| 20 | #include <string.h> | |
| 32c903ac | 21 | #include <time.h> |
| 589e7c1d SW |
22 | |
| 23 | #include "chars.h" | |
| 24 | #include "out.h" | |
| 25 | #include "term.h" | |
| 26 | #include "man.h" | |
| 27 | #include "mdoc.h" | |
| 28 | #include "main.h" | |
| 29 | ||
| 30 | /* FIXME: accomodate non-breaking, non-collapsing white-space. */ | |
| 31 | /* FIXME: accomodate non-breaking, collapsing white-space. */ | |
| 32 | ||
| 33 | static struct termp *term_alloc(enum termenc); | |
| 34 | static void term_free(struct termp *); | |
| 35 | ||
| 36 | static void do_escaped(struct termp *, const char **); | |
| 37 | static void do_special(struct termp *, | |
| 38 | const char *, size_t); | |
| 39 | static void do_reserved(struct termp *, | |
| 40 | const char *, size_t); | |
| 41 | static void buffer(struct termp *, char); | |
| 42 | static void encode(struct termp *, char); | |
| 43 | ||
| 44 | ||
| 45 | void * | |
| 46 | ascii_alloc(void) | |
| 47 | { | |
| 48 | ||
| 49 | return(term_alloc(TERMENC_ASCII)); | |
| 50 | } | |
| 51 | ||
| 52 | ||
| 53 | void | |
| 54 | terminal_free(void *arg) | |
| 55 | { | |
| 56 | ||
| 57 | term_free((struct termp *)arg); | |
| 58 | } | |
| 59 | ||
| 60 | ||
| 61 | static void | |
| 62 | term_free(struct termp *p) | |
| 63 | { | |
| 64 | ||
| 65 | if (p->buf) | |
| 66 | free(p->buf); | |
| 67 | if (p->symtab) | |
| 68 | chars_free(p->symtab); | |
| 69 | ||
| 70 | free(p); | |
| 71 | } | |
| 72 | ||
| 73 | ||
| 74 | static struct termp * | |
| 75 | term_alloc(enum termenc enc) | |
| 76 | { | |
| 77 | struct termp *p; | |
| 78 | ||
| 32c903ac SW |
79 | p = calloc(1, sizeof(struct termp)); |
| 80 | if (NULL == p) { | |
| 81 | perror(NULL); | |
| 82 | exit(EXIT_FAILURE); | |
| 83 | } | |
| 589e7c1d SW |
84 | p->maxrmargin = 78; |
| 85 | p->enc = enc; | |
| 86 | return(p); | |
| 87 | } | |
| 88 | ||
| 89 | ||
| 90 | /* | |
| 91 | * Flush a line of text. A "line" is loosely defined as being something | |
| 92 | * that should be followed by a newline, regardless of whether it's | |
| 93 | * broken apart by newlines getting there. A line can also be a | |
| 94 | * fragment of a columnar list. | |
| 95 | * | |
| 96 | * Specifically, a line is whatever's in p->buf of length p->col, which | |
| 97 | * is zeroed after this function returns. | |
| 98 | * | |
| 99 | * The usage of termp:flags is as follows: | |
| 100 | * | |
| 101 | * - TERMP_NOLPAD: when beginning to write the line, don't left-pad the | |
| 102 | * offset value. This is useful when doing columnar lists where the | |
| 103 | * prior column has right-padded. | |
| 104 | * | |
| 105 | * - TERMP_NOBREAK: this is the most important and is used when making | |
| 106 | * columns. In short: don't print a newline and instead pad to the | |
| 107 | * right margin. Used in conjunction with TERMP_NOLPAD. | |
| 108 | * | |
| 109 | * - TERMP_TWOSPACE: when padding, make sure there are at least two | |
| 110 | * space characters of padding. Otherwise, rather break the line. | |
| 111 | * | |
| 112 | * - TERMP_DANGLE: don't newline when TERMP_NOBREAK is specified and | |
| 113 | * the line is overrun, and don't pad-right if it's underrun. | |
| 114 | * | |
| 115 | * - TERMP_HANG: like TERMP_DANGLE, but doesn't newline when | |
| 116 | * overruning, instead save the position and continue at that point | |
| 117 | * when the next invocation. | |
| 118 | * | |
| 119 | * In-line line breaking: | |
| 120 | * | |
| 121 | * If TERMP_NOBREAK is specified and the line overruns the right | |
| 122 | * margin, it will break and pad-right to the right margin after | |
| 123 | * writing. If maxrmargin is violated, it will break and continue | |
| 32c903ac SW |
124 | * writing from the right-margin, which will lead to the above scenario |
| 125 | * upon exit. Otherwise, the line will break at the right margin. | |
| 589e7c1d SW |
126 | */ |
| 127 | void | |
| 128 | term_flushln(struct termp *p) | |
| 129 | { | |
| 32c903ac SW |
130 | int i; /* current input position in p->buf */ |
| 131 | size_t vis; /* current visual position on output */ | |
| 132 | size_t vbl; /* number of blanks to prepend to output */ | |
| 133 | size_t vsz; /* visual characters to write to output */ | |
| 134 | size_t bp; /* visual right border position */ | |
| 135 | int j; /* temporary loop index */ | |
| 136 | size_t maxvis, mmax; | |
| 589e7c1d SW |
137 | static int overstep = 0; |
| 138 | ||
| 139 | /* | |
| 140 | * First, establish the maximum columns of "visible" content. | |
| 141 | * This is usually the difference between the right-margin and | |
| 142 | * an indentation, but can be, for tagged lists or columns, a | |
| 143 | * small set of values. | |
| 144 | */ | |
| 145 | ||
| 146 | assert(p->offset < p->rmargin); | |
| 589e7c1d | 147 | |
| 32c903ac SW |
148 | maxvis = (int)(p->rmargin - p->offset) - overstep < 0 ? |
| 149 | /* LINTED */ | |
| 150 | 0 : p->rmargin - p->offset - overstep; | |
| 151 | mmax = (int)(p->maxrmargin - p->offset) - overstep < 0 ? | |
| 152 | /* LINTED */ | |
| 153 | 0 : p->maxrmargin - p->offset - overstep; | |
| 589e7c1d SW |
154 | |
| 155 | bp = TERMP_NOBREAK & p->flags ? mmax : maxvis; | |
| 32c903ac SW |
156 | |
| 157 | /* | |
| 158 | * FIXME: if bp is zero, we still output the first word before | |
| 159 | * breaking the line. | |
| 160 | */ | |
| 161 | ||
| 589e7c1d SW |
162 | vis = 0; |
| 163 | ||
| 164 | /* | |
| 165 | * If in the standard case (left-justified), then begin with our | |
| 166 | * indentation, otherwise (columns, etc.) just start spitting | |
| 167 | * out text. | |
| 168 | */ | |
| 169 | ||
| 170 | if ( ! (p->flags & TERMP_NOLPAD)) | |
| 171 | /* LINTED */ | |
| 172 | for (j = 0; j < (int)p->offset; j++) | |
| 173 | putchar(' '); | |
| 174 | ||
| 175 | for (i = 0; i < (int)p->col; i++) { | |
| 176 | /* | |
| 177 | * Count up visible word characters. Control sequences | |
| 178 | * (starting with the CSI) aren't counted. A space | |
| 179 | * generates a non-printing word, which is valid (the | |
| 180 | * space is printed according to regular spacing rules). | |
| 181 | */ | |
| 182 | ||
| 183 | /* LINTED */ | |
| 184 | for (j = i, vsz = 0; j < (int)p->col; j++) { | |
| 185 | if (j && ' ' == p->buf[j]) | |
| 186 | break; | |
| 187 | else if (8 == p->buf[j]) | |
| 188 | vsz--; | |
| 189 | else | |
| 190 | vsz++; | |
| 191 | } | |
| 192 | ||
| 193 | /* | |
| 194 | * Choose the number of blanks to prepend: no blank at the | |
| 195 | * beginning of a line, one between words -- but do not | |
| 196 | * actually write them yet. | |
| 197 | */ | |
| 198 | vbl = (size_t)(0 == vis ? 0 : 1); | |
| 199 | ||
| 200 | /* | |
| 201 | * Find out whether we would exceed the right margin. | |
| 202 | * If so, break to the next line. (TODO: hyphenate) | |
| 203 | * Otherwise, write the chosen number of blanks now. | |
| 204 | */ | |
| 205 | if (vis && vis + vbl + vsz > bp) { | |
| 206 | putchar('\n'); | |
| 207 | if (TERMP_NOBREAK & p->flags) { | |
| 208 | for (j = 0; j < (int)p->rmargin; j++) | |
| 209 | putchar(' '); | |
| 210 | vis = p->rmargin - p->offset; | |
| 211 | } else { | |
| 212 | for (j = 0; j < (int)p->offset; j++) | |
| 213 | putchar(' '); | |
| 214 | vis = 0; | |
| 215 | } | |
| 216 | /* Remove the overstep width. */ | |
| cbce6d97 SW |
217 | bp += (int)/* LINTED */ |
| 218 | overstep; | |
| 589e7c1d SW |
219 | overstep = 0; |
| 220 | } else { | |
| 221 | for (j = 0; j < (int)vbl; j++) | |
| 222 | putchar(' '); | |
| 223 | vis += vbl; | |
| 224 | } | |
| 225 | ||
| 226 | /* | |
| 227 | * Finally, write out the word. | |
| 228 | */ | |
| 229 | for ( ; i < (int)p->col; i++) { | |
| 230 | if (' ' == p->buf[i]) | |
| 231 | break; | |
| 232 | putchar(p->buf[i]); | |
| 233 | } | |
| 234 | vis += vsz; | |
| 235 | } | |
| cbce6d97 | 236 | |
| 589e7c1d | 237 | p->col = 0; |
| cbce6d97 | 238 | overstep = 0; |
| 589e7c1d SW |
239 | |
| 240 | if ( ! (TERMP_NOBREAK & p->flags)) { | |
| 241 | putchar('\n'); | |
| 242 | return; | |
| 243 | } | |
| 244 | ||
| 589e7c1d SW |
245 | if (TERMP_HANG & p->flags) { |
| 246 | /* We need one blank after the tag. */ | |
| 247 | overstep = /* LINTED */ | |
| 248 | vis - maxvis + 1; | |
| 249 | ||
| 250 | /* | |
| 251 | * Behave exactly the same way as groff: | |
| 252 | * If we have overstepped the margin, temporarily move | |
| 253 | * it to the right and flag the rest of the line to be | |
| 254 | * shorter. | |
| 255 | * If we landed right at the margin, be happy. | |
| 256 | * If we are one step before the margin, temporarily | |
| 257 | * move it one step LEFT and flag the rest of the line | |
| 258 | * to be longer. | |
| 259 | */ | |
| 260 | if (overstep >= -1) { | |
| 261 | assert((int)maxvis + overstep >= 0); | |
| 262 | /* LINTED */ | |
| 263 | maxvis += overstep; | |
| 264 | } else | |
| 265 | overstep = 0; | |
| 266 | ||
| 267 | } else if (TERMP_DANGLE & p->flags) | |
| 268 | return; | |
| 269 | ||
| 270 | /* Right-pad. */ | |
| 271 | if (maxvis > vis + /* LINTED */ | |
| 272 | ((TERMP_TWOSPACE & p->flags) ? 1 : 0)) | |
| 273 | for ( ; vis < maxvis; vis++) | |
| 274 | putchar(' '); | |
| 275 | else { /* ...or newline break. */ | |
| 276 | putchar('\n'); | |
| 277 | for (i = 0; i < (int)p->rmargin; i++) | |
| 278 | putchar(' '); | |
| 279 | } | |
| 280 | } | |
| 281 | ||
| 282 | ||
| 283 | /* | |
| 284 | * A newline only breaks an existing line; it won't assert vertical | |
| 285 | * space. All data in the output buffer is flushed prior to the newline | |
| 286 | * assertion. | |
| 287 | */ | |
| 288 | void | |
| 289 | term_newln(struct termp *p) | |
| 290 | { | |
| 291 | ||
| 292 | p->flags |= TERMP_NOSPACE; | |
| 293 | if (0 == p->col) { | |
| 294 | p->flags &= ~TERMP_NOLPAD; | |
| 295 | return; | |
| 296 | } | |
| 297 | term_flushln(p); | |
| 298 | p->flags &= ~TERMP_NOLPAD; | |
| 299 | } | |
| 300 | ||
| 301 | ||
| 302 | /* | |
| 303 | * Asserts a vertical space (a full, empty line-break between lines). | |
| 304 | * Note that if used twice, this will cause two blank spaces and so on. | |
| 305 | * All data in the output buffer is flushed prior to the newline | |
| 306 | * assertion. | |
| 307 | */ | |
| 308 | void | |
| 309 | term_vspace(struct termp *p) | |
| 310 | { | |
| 311 | ||
| 312 | term_newln(p); | |
| 313 | putchar('\n'); | |
| 314 | } | |
| 315 | ||
| 316 | ||
| 317 | static void | |
| 318 | do_special(struct termp *p, const char *word, size_t len) | |
| 319 | { | |
| 320 | const char *rhs; | |
| 321 | size_t sz; | |
| 322 | int i; | |
| 323 | ||
| 324 | rhs = chars_a2ascii(p->symtab, word, len, &sz); | |
| 325 | ||
| 326 | if (NULL == rhs) { | |
| 327 | #if 0 | |
| 328 | fputs("Unknown special character: ", stderr); | |
| 329 | for (i = 0; i < (int)len; i++) | |
| 330 | fputc(word[i], stderr); | |
| 331 | fputc('\n', stderr); | |
| 332 | #endif | |
| 333 | return; | |
| 334 | } | |
| 335 | for (i = 0; i < (int)sz; i++) | |
| 336 | encode(p, rhs[i]); | |
| 337 | } | |
| 338 | ||
| 339 | ||
| 340 | static void | |
| 341 | do_reserved(struct termp *p, const char *word, size_t len) | |
| 342 | { | |
| 343 | const char *rhs; | |
| 344 | size_t sz; | |
| 345 | int i; | |
| 346 | ||
| 347 | rhs = chars_a2res(p->symtab, word, len, &sz); | |
| 348 | ||
| 349 | if (NULL == rhs) { | |
| 350 | #if 0 | |
| 351 | fputs("Unknown reserved word: ", stderr); | |
| 352 | for (i = 0; i < (int)len; i++) | |
| 353 | fputc(word[i], stderr); | |
| 354 | fputc('\n', stderr); | |
| 355 | #endif | |
| 356 | return; | |
| 357 | } | |
| 358 | for (i = 0; i < (int)sz; i++) | |
| 359 | encode(p, rhs[i]); | |
| 360 | } | |
| 361 | ||
| 362 | ||
| 363 | /* | |
| 364 | * Handle an escape sequence: determine its length and pass it to the | |
| 365 | * escape-symbol look table. Note that we assume mdoc(3) has validated | |
| 366 | * the escape sequence (we assert upon badly-formed escape sequences). | |
| 367 | */ | |
| 368 | static void | |
| 369 | do_escaped(struct termp *p, const char **word) | |
| 370 | { | |
| 371 | int j, type; | |
| 372 | const char *wp; | |
| 373 | ||
| 374 | wp = *word; | |
| 375 | type = 1; | |
| 376 | ||
| 377 | if (0 == *(++wp)) { | |
| 378 | *word = wp; | |
| 379 | return; | |
| 380 | } | |
| 381 | ||
| 382 | if ('(' == *wp) { | |
| 383 | wp++; | |
| 384 | if (0 == *wp || 0 == *(wp + 1)) { | |
| 385 | *word = 0 == *wp ? wp : wp + 1; | |
| 386 | return; | |
| 387 | } | |
| 388 | ||
| 389 | do_special(p, wp, 2); | |
| 390 | *word = ++wp; | |
| 391 | return; | |
| 392 | ||
| 393 | } else if ('*' == *wp) { | |
| 394 | if (0 == *(++wp)) { | |
| 395 | *word = wp; | |
| 396 | return; | |
| 397 | } | |
| 398 | ||
| 399 | switch (*wp) { | |
| 400 | case ('('): | |
| 401 | wp++; | |
| 402 | if (0 == *wp || 0 == *(wp + 1)) { | |
| 403 | *word = 0 == *wp ? wp : wp + 1; | |
| 404 | return; | |
| 405 | } | |
| 406 | ||
| 407 | do_reserved(p, wp, 2); | |
| 408 | *word = ++wp; | |
| 409 | return; | |
| 410 | case ('['): | |
| 411 | type = 0; | |
| 412 | break; | |
| 413 | default: | |
| 414 | do_reserved(p, wp, 1); | |
| 415 | *word = wp; | |
| 416 | return; | |
| 417 | } | |
| 418 | ||
| 419 | } else if ('f' == *wp) { | |
| 420 | if (0 == *(++wp)) { | |
| 421 | *word = wp; | |
| 422 | return; | |
| 423 | } | |
| 424 | ||
| 425 | switch (*wp) { | |
| 426 | case ('B'): | |
| 427 | p->bold++; | |
| 428 | break; | |
| 429 | case ('I'): | |
| 430 | p->under++; | |
| 431 | break; | |
| 432 | case ('P'): | |
| 433 | /* FALLTHROUGH */ | |
| 434 | case ('R'): | |
| 435 | p->bold = p->under = 0; | |
| 436 | break; | |
| 437 | default: | |
| 438 | break; | |
| 439 | } | |
| 440 | ||
| 441 | *word = wp; | |
| 442 | return; | |
| 443 | ||
| 444 | } else if ('[' != *wp) { | |
| 445 | do_special(p, wp, 1); | |
| 446 | *word = wp; | |
| 447 | return; | |
| 448 | } | |
| 449 | ||
| 450 | wp++; | |
| 451 | for (j = 0; *wp && ']' != *wp; wp++, j++) | |
| 452 | /* Loop... */ ; | |
| 453 | ||
| 454 | if (0 == *wp) { | |
| 455 | *word = wp; | |
| 456 | return; | |
| 457 | } | |
| 458 | ||
| 459 | if (type) | |
| 460 | do_special(p, wp - j, (size_t)j); | |
| 461 | else | |
| 462 | do_reserved(p, wp - j, (size_t)j); | |
| 463 | *word = wp; | |
| 464 | } | |
| 465 | ||
| 466 | ||
| 467 | /* | |
| 468 | * Handle pwords, partial words, which may be either a single word or a | |
| 469 | * phrase that cannot be broken down (such as a literal string). This | |
| 470 | * handles word styling. | |
| 471 | */ | |
| 472 | void | |
| 473 | term_word(struct termp *p, const char *word) | |
| 474 | { | |
| 475 | const char *sv; | |
| 476 | ||
| 477 | sv = word; | |
| 478 | ||
| 479 | if (word[0] && 0 == word[1]) | |
| 480 | switch (word[0]) { | |
| 481 | case('.'): | |
| 482 | /* FALLTHROUGH */ | |
| 483 | case(','): | |
| 484 | /* FALLTHROUGH */ | |
| 485 | case(';'): | |
| 486 | /* FALLTHROUGH */ | |
| 487 | case(':'): | |
| 488 | /* FALLTHROUGH */ | |
| 489 | case('?'): | |
| 490 | /* FALLTHROUGH */ | |
| 491 | case('!'): | |
| 492 | /* FALLTHROUGH */ | |
| 493 | case(')'): | |
| 494 | /* FALLTHROUGH */ | |
| 495 | case(']'): | |
| 496 | /* FALLTHROUGH */ | |
| 497 | case('}'): | |
| 498 | if ( ! (TERMP_IGNDELIM & p->flags)) | |
| 499 | p->flags |= TERMP_NOSPACE; | |
| 500 | break; | |
| 501 | default: | |
| 502 | break; | |
| 503 | } | |
| 504 | ||
| 505 | if ( ! (TERMP_NOSPACE & p->flags)) | |
| 506 | buffer(p, ' '); | |
| 507 | ||
| 508 | if ( ! (p->flags & TERMP_NONOSPACE)) | |
| 509 | p->flags &= ~TERMP_NOSPACE; | |
| 510 | ||
| 511 | for ( ; *word; word++) | |
| 512 | if ('\\' != *word) | |
| 513 | encode(p, *word); | |
| 514 | else | |
| 515 | do_escaped(p, &word); | |
| 516 | ||
| 517 | if (sv[0] && 0 == sv[1]) | |
| 518 | switch (sv[0]) { | |
| 519 | case('('): | |
| 520 | /* FALLTHROUGH */ | |
| 521 | case('['): | |
| 522 | /* FALLTHROUGH */ | |
| 523 | case('{'): | |
| 524 | p->flags |= TERMP_NOSPACE; | |
| 525 | break; | |
| 526 | default: | |
| 527 | break; | |
| 528 | } | |
| 529 | } | |
| 530 | ||
| 531 | ||
| 532 | /* | |
| 533 | * Insert a single character into the line-buffer. If the buffer's | |
| 534 | * space is exceeded, then allocate more space by doubling the buffer | |
| 535 | * size. | |
| 536 | */ | |
| 537 | static void | |
| 538 | buffer(struct termp *p, char c) | |
| 539 | { | |
| 540 | size_t s; | |
| 541 | ||
| 542 | if (p->col + 1 >= p->maxcols) { | |
| 543 | if (0 == p->maxcols) | |
| 544 | p->maxcols = 256; | |
| 545 | s = p->maxcols * 2; | |
| 546 | p->buf = realloc(p->buf, s); | |
| 32c903ac SW |
547 | if (NULL == p->buf) { |
| 548 | perror(NULL); | |
| 549 | exit(EXIT_FAILURE); | |
| 550 | } | |
| 589e7c1d SW |
551 | p->maxcols = s; |
| 552 | } | |
| 553 | p->buf[(int)(p->col)++] = c; | |
| 554 | } | |
| 555 | ||
| 556 | ||
| 557 | static void | |
| 558 | encode(struct termp *p, char c) | |
| 559 | { | |
| 560 | ||
| 561 | if (' ' != c) { | |
| 589e7c1d SW |
562 | if (p->under) { |
| 563 | buffer(p, '_'); | |
| 564 | buffer(p, 8); | |
| 565 | } | |
| cbce6d97 SW |
566 | if (p->bold) { |
| 567 | buffer(p, c); | |
| 568 | buffer(p, 8); | |
| 569 | } | |
| 589e7c1d SW |
570 | } |
| 571 | buffer(p, c); | |
| 572 | } | |
| 573 | ||
| 574 | ||
| 575 | size_t | |
| 576 | term_vspan(const struct roffsu *su) | |
| 577 | { | |
| 578 | double r; | |
| 579 | ||
| 580 | switch (su->unit) { | |
| 581 | case (SCALE_CM): | |
| 582 | r = su->scale * 2; | |
| 583 | break; | |
| 584 | case (SCALE_IN): | |
| 585 | r = su->scale * 6; | |
| 586 | break; | |
| 587 | case (SCALE_PC): | |
| 588 | r = su->scale; | |
| 589 | break; | |
| 590 | case (SCALE_PT): | |
| 591 | r = su->scale / 8; | |
| 592 | break; | |
| 593 | case (SCALE_MM): | |
| 594 | r = su->scale / 1000; | |
| 595 | break; | |
| 596 | case (SCALE_VS): | |
| 597 | r = su->scale; | |
| 598 | break; | |
| 599 | default: | |
| 600 | r = su->scale - 1; | |
| 601 | break; | |
| 602 | } | |
| 603 | ||
| 604 | if (r < 0.0) | |
| 605 | r = 0.0; | |
| 606 | return(/* LINTED */(size_t) | |
| 607 | r); | |
| 608 | } | |
| 609 | ||
| 610 | ||
| 611 | size_t | |
| 612 | term_hspan(const struct roffsu *su) | |
| 613 | { | |
| 614 | double r; | |
| 615 | ||
| 616 | /* XXX: CM, IN, and PT are approximations. */ | |
| 617 | ||
| 618 | switch (su->unit) { | |
| 619 | case (SCALE_CM): | |
| 620 | r = 4 * su->scale; | |
| 621 | break; | |
| 622 | case (SCALE_IN): | |
| 623 | /* XXX: this is an approximation. */ | |
| 624 | r = 10 * su->scale; | |
| 625 | break; | |
| 626 | case (SCALE_PC): | |
| 627 | r = (10 * su->scale) / 6; | |
| 628 | break; | |
| 629 | case (SCALE_PT): | |
| 630 | r = (10 * su->scale) / 72; | |
| 631 | break; | |
| 632 | case (SCALE_MM): | |
| 633 | r = su->scale / 1000; /* FIXME: double-check. */ | |
| 634 | break; | |
| 635 | case (SCALE_VS): | |
| 636 | r = su->scale * 2 - 1; /* FIXME: double-check. */ | |
| 637 | break; | |
| 638 | default: | |
| 639 | r = su->scale; | |
| 640 | break; | |
| 641 | } | |
| 642 | ||
| 643 | if (r < 0.0) | |
| 644 | r = 0.0; | |
| 645 | return((size_t)/* LINTED */ | |
| 646 | r); | |
| 647 | } |