Annotation of mandoc/term.c, Revision 1.143
1.143 ! kristaps 1: /* $Id: term.c,v 1.142 2010/06/07 20:57:09 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.142 kristaps 39: #define PS_CHAR_WIDTH 6
40: #define PS_CHAR_HEIGHT 12
41: #define PS_CHAR_TOPMARG (792 - 24)
42: #define PS_CHAR_TOP (PS_CHAR_TOPMARG - 36)
43: #define PS_CHAR_LEFT 36
44: #define PS_CHAR_BOTMARG 24
45: #define PS_CHAR_BOT (PS_CHAR_BOTMARG + 36)
46:
47: static struct termp *alloc(char *, enum termenc, enum termtype);
1.71 kristaps 48: static void term_free(struct termp *);
1.125 kristaps 49: static void spec(struct termp *, const char *, size_t);
50: static void res(struct termp *, const char *, size_t);
51: static void buffera(struct termp *, const char *, size_t);
52: static void bufferc(struct termp *, char);
53: static void adjbuf(struct termp *p, size_t);
54: static void encode(struct termp *, const char *, size_t);
1.142 kristaps 55: static void advance(struct termp *, size_t);
56: static void endline(struct termp *);
57: static void letter(struct termp *, char);
58: static void pageopen(struct termp *);
1.1 kristaps 59:
60:
1.71 kristaps 61: void *
1.141 kristaps 62: ascii_alloc(char *outopts)
1.10 kristaps 63: {
1.1 kristaps 64:
1.142 kristaps 65: return(alloc(outopts, TERMENC_ASCII, TERMTYPE_CHAR));
66: }
67:
68:
69: void *
70: ps_alloc(void)
71: {
72:
73: return(alloc(NULL, TERMENC_ASCII, TERMTYPE_PS));
1.1 kristaps 74: }
75:
76:
1.99 kristaps 77: void
1.71 kristaps 78: terminal_free(void *arg)
1.11 kristaps 79: {
80:
1.71 kristaps 81: term_free((struct termp *)arg);
1.11 kristaps 82: }
83:
84:
1.71 kristaps 85: static void
86: term_free(struct termp *p)
1.14 kristaps 87: {
88:
1.71 kristaps 89: if (p->buf)
90: free(p->buf);
1.102 kristaps 91: if (p->symtab)
1.101 kristaps 92: chars_free(p->symtab);
1.142 kristaps 93: free(p);
94: }
95:
96:
97: /*
98: * Push a single letter into our output engine.
99: */
100: static void
101: letter(struct termp *p, char c)
102: {
103:
104: if (TERMTYPE_CHAR == p->type) {
105: /*
106: * If using the terminal device, just push the letter
107: * out into the screen.
108: */
109: putchar(c);
110: return;
111: }
112:
113: if ( ! (PS_INLINE & p->psstate)) {
114: /*
115: * If we're not in a PostScript "word" context, then
116: * open one now at the current cursor.
117: */
118: printf("%zu %zu moveto\n", p->pscol, p->psrow);
119: putchar('(');
120: p->psstate |= PS_INLINE;
121: }
122:
123: /*
124: * We need to escape these characters as per the PostScript
125: * specification. We would also escape non-graphable characters
126: * (like tabs), but none of them would get to this point and
127: * it's superfluous to abort() on them.
128: */
129:
130: switch (c) {
131: case ('('):
132: /* FALLTHROUGH */
133: case (')'):
134: /* FALLTHROUGH */
135: case ('\\'):
136: putchar('\\');
137: break;
138: default:
139: break;
140: }
141:
142: /* Write the character and adjust where we are on the page. */
143: putchar(c);
144: p->pscol += PS_CHAR_WIDTH;
145: }
146:
147:
148: /*
149: * Begin a "terminal" context. Since terminal encompasses PostScript,
150: * the actual terminal, etc., there are a few things we can do here.
151: */
152: void
153: term_begin(struct termp *p, term_margin head,
154: term_margin foot, const void *arg)
155: {
156:
157: p->headf = head;
158: p->footf = foot;
159: p->argf = arg;
160:
161: if (TERMTYPE_CHAR == p->type) {
162: /* Emit the header and be done. */
163: (*p->headf)(p, p->argf);
164: return;
165: }
166:
167: /*
168: * Emit the standard PostScript prologue, set our initial page
169: * position, then run pageopen() on the initial page.
170: */
171:
172: printf("%s\n", "%!PS");
173: printf("%s\n", "/Courier");
174: printf("%s\n", "10 selectfont");
175:
176: p->pspage = 1;
177: p->psstate = 0;
178: pageopen(p);
179: }
180:
181:
182: /*
183: * Open a page. This is only used for -Tps at the moment. It opens a
184: * page context, printing the header and the footer. THE OUTPUT BUFFER
185: * MUST BE EMPTY. If it is not, output will ghost on the next line and
186: * we'll be all gross and out of state.
187: */
188: static void
189: pageopen(struct termp *p)
190: {
191:
192: assert(TERMTYPE_PS == p->type);
193: assert(0 == p->psstate);
194:
195: p->pscol = PS_CHAR_LEFT;
196: p->psrow = PS_CHAR_TOPMARG;
197: p->psstate |= PS_MARGINS;
198:
199: (*p->headf)(p, p->argf);
200: endline(p);
201:
202: p->psstate &= ~PS_MARGINS;
203: assert(0 == p->psstate);
1.14 kristaps 204:
1.142 kristaps 205: p->pscol = PS_CHAR_LEFT;
206: p->psrow = PS_CHAR_BOTMARG;
207: p->psstate |= PS_MARGINS;
208:
209: (*p->footf)(p, p->argf);
210: endline(p);
211:
212: p->psstate &= ~PS_MARGINS;
213: assert(0 == p->psstate);
214:
215: p->pscol = PS_CHAR_LEFT;
216: p->psrow = PS_CHAR_TOP;
217:
218: }
219:
220:
221: void
222: term_end(struct termp *p)
223: {
224:
225: if (TERMTYPE_CHAR == p->type) {
226: (*p->footf)(p, p->argf);
227: return;
228: }
229:
230: printf("%s\n", "%%END");
231: }
232:
233:
234: static void
235: endline(struct termp *p)
236: {
237:
238: if (TERMTYPE_CHAR == p->type) {
239: putchar('\n');
240: return;
241: }
242:
243: if (PS_INLINE & p->psstate) {
244: printf(") show\n");
245: p->psstate &= ~PS_INLINE;
246: }
247:
248: if (PS_MARGINS & p->psstate)
249: return;
250:
251: p->pscol = PS_CHAR_LEFT;
252: if (p->psrow >= PS_CHAR_HEIGHT + PS_CHAR_BOT) {
253: p->psrow -= PS_CHAR_HEIGHT;
254: return;
255: }
256:
257: /*
258: * XXX: can't run pageopen() until we're certain a flushln() has
259: * occured, else the buf will reopen in an awkward state on the
260: * next line.
261: */
262: printf("showpage\n");
263: p->psrow = PS_CHAR_TOP;
264: }
265:
266:
267: /*
268: * Advance the output engine by a certain amount of whitespace.
269: */
270: static void
271: advance(struct termp *p, size_t len)
272: {
273: size_t i;
274:
275: if (TERMTYPE_CHAR == p->type) {
276: /* Just print whitespace on the terminal. */
277: for (i = 0; i < len; i++)
278: putchar(' ');
279: return;
280: }
281:
282: if (PS_INLINE & p->psstate) {
283: /* Dump out any existing line scope. */
284: printf(") show\n");
285: p->psstate &= ~PS_INLINE;
286: }
287:
288: p->pscol += len ? len * PS_CHAR_WIDTH : 0;
1.14 kristaps 289: }
290:
291:
1.71 kristaps 292: static struct termp *
1.142 kristaps 293: alloc(char *outopts, enum termenc enc, enum termtype type)
1.14 kristaps 294: {
1.141 kristaps 295: struct termp *p;
296: const char *toks[2];
297: char *v;
298: size_t width;
299:
300: toks[0] = "width";
301: toks[1] = NULL;
1.14 kristaps 302:
1.117 kristaps 303: p = calloc(1, sizeof(struct termp));
304: if (NULL == p) {
1.120 kristaps 305: perror(NULL);
1.117 kristaps 306: exit(EXIT_FAILURE);
307: }
1.141 kristaps 308:
1.142 kristaps 309: p->type = type;
1.138 schwarze 310: p->tabwidth = 5;
1.71 kristaps 311: p->enc = enc;
1.142 kristaps 312:
1.141 kristaps 313: width = 80;
314:
315: while (outopts && *outopts)
316: switch (getsubopt(&outopts, UNCONST(toks), &v)) {
317: case (0):
1.143 ! kristaps 318: width = (size_t)atoi(v);
1.141 kristaps 319: break;
320: default:
321: break;
322: }
323:
1.134 joerg 324: /* Enforce some lower boundary. */
325: if (width < 60)
326: width = 60;
327: p->defrmargin = width - 2;
1.71 kristaps 328: return(p);
1.14 kristaps 329: }
330:
331:
1.71 kristaps 332: /*
333: * Flush a line of text. A "line" is loosely defined as being something
334: * that should be followed by a newline, regardless of whether it's
335: * broken apart by newlines getting there. A line can also be a
1.130 kristaps 336: * fragment of a columnar list (`Bl -tag' or `Bl -column'), which does
337: * not have a trailing newline.
1.71 kristaps 338: *
1.130 kristaps 339: * The following flags may be specified:
1.71 kristaps 340: *
341: * - TERMP_NOLPAD: when beginning to write the line, don't left-pad the
342: * offset value. This is useful when doing columnar lists where the
343: * prior column has right-padded.
344: *
345: * - TERMP_NOBREAK: this is the most important and is used when making
346: * columns. In short: don't print a newline and instead pad to the
347: * right margin. Used in conjunction with TERMP_NOLPAD.
348: *
1.91 kristaps 349: * - TERMP_TWOSPACE: when padding, make sure there are at least two
350: * space characters of padding. Otherwise, rather break the line.
351: *
1.84 kristaps 352: * - TERMP_DANGLE: don't newline when TERMP_NOBREAK is specified and
353: * the line is overrun, and don't pad-right if it's underrun.
354: *
355: * - TERMP_HANG: like TERMP_DANGLE, but doesn't newline when
356: * overruning, instead save the position and continue at that point
357: * when the next invocation.
1.71 kristaps 358: *
359: * In-line line breaking:
360: *
361: * If TERMP_NOBREAK is specified and the line overruns the right
362: * margin, it will break and pad-right to the right margin after
363: * writing. If maxrmargin is violated, it will break and continue
1.114 kristaps 364: * writing from the right-margin, which will lead to the above scenario
365: * upon exit. Otherwise, the line will break at the right margin.
1.71 kristaps 366: */
367: void
368: term_flushln(struct termp *p)
1.53 kristaps 369: {
1.114 kristaps 370: int i; /* current input position in p->buf */
371: size_t vis; /* current visual position on output */
372: size_t vbl; /* number of blanks to prepend to output */
1.136 schwarze 373: size_t vend; /* end of word visual position on output */
1.114 kristaps 374: size_t bp; /* visual right border position */
375: int j; /* temporary loop index */
1.140 kristaps 376: int jhy; /* last hyphen before line overflow */
1.114 kristaps 377: size_t maxvis, mmax;
1.53 kristaps 378:
1.71 kristaps 379: /*
380: * First, establish the maximum columns of "visible" content.
381: * This is usually the difference between the right-margin and
382: * an indentation, but can be, for tagged lists or columns, a
1.115 kristaps 383: * small set of values.
1.71 kristaps 384: */
1.53 kristaps 385:
1.71 kristaps 386: assert(p->offset < p->rmargin);
1.92 kristaps 387:
1.129 kristaps 388: maxvis = (int)(p->rmargin - p->offset) - p->overstep < 0 ?
1.119 kristaps 389: /* LINTED */
1.129 kristaps 390: 0 : p->rmargin - p->offset - p->overstep;
391: mmax = (int)(p->maxrmargin - p->offset) - p->overstep < 0 ?
1.119 kristaps 392: /* LINTED */
1.129 kristaps 393: 0 : p->maxrmargin - p->offset - p->overstep;
1.92 kristaps 394:
1.71 kristaps 395: bp = TERMP_NOBREAK & p->flags ? mmax : maxvis;
1.115 kristaps 396:
1.136 schwarze 397: /*
398: * Indent the first line of a paragraph.
399: */
400: vbl = p->flags & TERMP_NOLPAD ? 0 : p->offset;
401:
1.115 kristaps 402: /*
403: * FIXME: if bp is zero, we still output the first word before
404: * breaking the line.
405: */
406:
1.136 schwarze 407: vis = vend = i = 0;
408: while (i < (int)p->col) {
1.71 kristaps 409:
410: /*
1.138 schwarze 411: * Handle literal tab characters.
412: */
413: for (j = i; j < (int)p->col; j++) {
414: if ('\t' != p->buf[j])
415: break;
416: vend = (vis/p->tabwidth+1)*p->tabwidth;
417: vbl += vend - vis;
418: vis = vend;
419: }
420:
421: /*
1.71 kristaps 422: * Count up visible word characters. Control sequences
423: * (starting with the CSI) aren't counted. A space
424: * generates a non-printing word, which is valid (the
425: * space is printed according to regular spacing rules).
426: */
427:
428: /* LINTED */
1.140 kristaps 429: for (jhy = 0; j < (int)p->col; j++) {
1.138 schwarze 430: if ((j && ' ' == p->buf[j]) || '\t' == p->buf[j])
1.71 kristaps 431: break;
1.140 kristaps 432: if (8 != p->buf[j]) {
433: if (vend > vis && vend < bp &&
434: ASCII_HYPH == p->buf[j])
435: jhy = j;
436: vend++;
437: } else
1.136 schwarze 438: vend--;
1.71 kristaps 439: }
1.53 kristaps 440:
1.71 kristaps 441: /*
1.81 kristaps 442: * Find out whether we would exceed the right margin.
1.136 schwarze 443: * If so, break to the next line.
1.81 kristaps 444: */
1.140 kristaps 445: if (vend > bp && 0 == jhy && vis > 0) {
1.136 schwarze 446: vend -= vis;
1.142 kristaps 447: endline(p);
1.81 kristaps 448: if (TERMP_NOBREAK & p->flags) {
1.139 schwarze 449: p->viscol = p->rmargin;
1.142 kristaps 450: advance(p, p->rmargin);
1.136 schwarze 451: vend += p->rmargin - p->offset;
1.81 kristaps 452: } else {
1.139 schwarze 453: p->viscol = 0;
1.136 schwarze 454: vbl = p->offset;
1.81 kristaps 455: }
1.130 kristaps 456:
1.129 kristaps 457: /* Remove the p->overstep width. */
1.130 kristaps 458:
1.112 kristaps 459: bp += (int)/* LINTED */
1.129 kristaps 460: p->overstep;
461: p->overstep = 0;
1.71 kristaps 462: }
1.53 kristaps 463:
1.138 schwarze 464: /*
465: * Skip leading tabs, they were handled above.
466: */
467: while (i < (int)p->col && '\t' == p->buf[i])
468: i++;
469:
1.130 kristaps 470: /* Write out the [remaining] word. */
1.136 schwarze 471: for ( ; i < (int)p->col; i++) {
1.140 kristaps 472: if (vend > bp && jhy > 0 && i > jhy)
473: break;
1.138 schwarze 474: if ('\t' == p->buf[i])
475: break;
1.136 schwarze 476: if (' ' == p->buf[i]) {
477: while (' ' == p->buf[i]) {
478: vbl++;
479: i++;
480: }
1.71 kristaps 481: break;
1.136 schwarze 482: }
483: if (ASCII_NBRSP == p->buf[i]) {
484: vbl++;
485: continue;
486: }
1.130 kristaps 487:
1.136 schwarze 488: /*
489: * Now we definitely know there will be
490: * printable characters to output,
491: * so write preceding white space now.
492: */
493: if (vbl) {
1.142 kristaps 494: advance(p, vbl);
1.139 schwarze 495: p->viscol += vbl;
1.136 schwarze 496: vbl = 0;
497: }
1.140 kristaps 498:
499: if (ASCII_HYPH == p->buf[i])
1.142 kristaps 500: letter(p, '-');
1.140 kristaps 501: else
1.142 kristaps 502: letter(p, p->buf[i]);
1.140 kristaps 503:
1.139 schwarze 504: p->viscol += 1;
1.136 schwarze 505: }
506: vend += vbl;
507: vis = vend;
1.71 kristaps 508: }
1.111 kristaps 509:
1.91 kristaps 510: p->col = 0;
1.129 kristaps 511: p->overstep = 0;
1.15 kristaps 512:
1.91 kristaps 513: if ( ! (TERMP_NOBREAK & p->flags)) {
1.139 schwarze 514: p->viscol = 0;
1.142 kristaps 515: endline(p);
1.15 kristaps 516: return;
1.71 kristaps 517: }
1.15 kristaps 518:
1.91 kristaps 519: if (TERMP_HANG & p->flags) {
520: /* We need one blank after the tag. */
1.129 kristaps 521: p->overstep = /* LINTED */
1.92 kristaps 522: vis - maxvis + 1;
1.91 kristaps 523:
524: /*
525: * Behave exactly the same way as groff:
1.92 kristaps 526: * If we have overstepped the margin, temporarily move
527: * it to the right and flag the rest of the line to be
528: * shorter.
1.91 kristaps 529: * If we landed right at the margin, be happy.
1.92 kristaps 530: * If we are one step before the margin, temporarily
531: * move it one step LEFT and flag the rest of the line
532: * to be longer.
1.91 kristaps 533: */
1.129 kristaps 534: if (p->overstep >= -1) {
535: assert((int)maxvis + p->overstep >= 0);
1.92 kristaps 536: /* LINTED */
1.129 kristaps 537: maxvis += p->overstep;
1.92 kristaps 538: } else
1.129 kristaps 539: p->overstep = 0;
1.91 kristaps 540:
541: } else if (TERMP_DANGLE & p->flags)
542: return;
1.15 kristaps 543:
1.92 kristaps 544: /* Right-pad. */
545: if (maxvis > vis + /* LINTED */
1.139 schwarze 546: ((TERMP_TWOSPACE & p->flags) ? 1 : 0)) {
547: p->viscol += maxvis - vis;
1.142 kristaps 548: advance(p, maxvis - vis);
549: vis += (maxvis - vis);
1.139 schwarze 550: } else { /* ...or newline break. */
1.142 kristaps 551: endline(p);
1.139 schwarze 552: p->viscol = p->rmargin;
1.142 kristaps 553: advance(p, p->rmargin);
1.91 kristaps 554: }
1.15 kristaps 555: }
556:
557:
1.71 kristaps 558: /*
559: * A newline only breaks an existing line; it won't assert vertical
560: * space. All data in the output buffer is flushed prior to the newline
561: * assertion.
562: */
563: void
564: term_newln(struct termp *p)
1.15 kristaps 565: {
566:
1.71 kristaps 567: p->flags |= TERMP_NOSPACE;
1.139 schwarze 568: if (0 == p->col && 0 == p->viscol) {
1.71 kristaps 569: p->flags &= ~TERMP_NOLPAD;
1.15 kristaps 570: return;
1.16 kristaps 571: }
1.71 kristaps 572: term_flushln(p);
573: p->flags &= ~TERMP_NOLPAD;
1.16 kristaps 574: }
575:
576:
1.71 kristaps 577: /*
578: * Asserts a vertical space (a full, empty line-break between lines).
579: * Note that if used twice, this will cause two blank spaces and so on.
580: * All data in the output buffer is flushed prior to the newline
581: * assertion.
582: */
583: void
584: term_vspace(struct termp *p)
1.16 kristaps 585: {
586:
1.62 kristaps 587: term_newln(p);
1.139 schwarze 588: p->viscol = 0;
1.142 kristaps 589: endline(p);
1.16 kristaps 590: }
591:
592:
1.71 kristaps 593: static void
1.125 kristaps 594: spec(struct termp *p, const char *word, size_t len)
1.17 kristaps 595: {
1.71 kristaps 596: const char *rhs;
597: size_t sz;
1.17 kristaps 598:
1.101 kristaps 599: rhs = chars_a2ascii(p->symtab, word, len, &sz);
1.125 kristaps 600: if (rhs)
601: encode(p, rhs, sz);
1.94 kristaps 602: }
603:
604:
605: static void
1.125 kristaps 606: res(struct termp *p, const char *word, size_t len)
1.94 kristaps 607: {
608: const char *rhs;
609: size_t sz;
610:
1.101 kristaps 611: rhs = chars_a2res(p->symtab, word, len, &sz);
1.125 kristaps 612: if (rhs)
613: encode(p, rhs, sz);
614: }
615:
616:
617: void
618: term_fontlast(struct termp *p)
619: {
620: enum termfont f;
621:
622: f = p->fontl;
623: p->fontl = p->fontq[p->fonti];
624: p->fontq[p->fonti] = f;
625: }
626:
627:
628: void
629: term_fontrepl(struct termp *p, enum termfont f)
630: {
631:
632: p->fontl = p->fontq[p->fonti];
633: p->fontq[p->fonti] = f;
634: }
635:
636:
637: void
638: term_fontpush(struct termp *p, enum termfont f)
639: {
640:
641: assert(p->fonti + 1 < 10);
642: p->fontl = p->fontq[p->fonti];
643: p->fontq[++p->fonti] = f;
644: }
645:
646:
647: const void *
648: term_fontq(struct termp *p)
649: {
650:
651: return(&p->fontq[p->fonti]);
652: }
653:
654:
655: enum termfont
656: term_fonttop(struct termp *p)
657: {
658:
659: return(p->fontq[p->fonti]);
660: }
661:
662:
663: void
664: term_fontpopq(struct termp *p, const void *key)
665: {
666:
667: while (p->fonti >= 0 && key != &p->fontq[p->fonti])
668: p->fonti--;
669: assert(p->fonti >= 0);
670: }
1.94 kristaps 671:
1.125 kristaps 672:
673: void
674: term_fontpop(struct termp *p)
675: {
676:
677: assert(p->fonti);
678: p->fonti--;
1.17 kristaps 679: }
680:
681:
1.71 kristaps 682: /*
683: * Handle pwords, partial words, which may be either a single word or a
684: * phrase that cannot be broken down (such as a literal string). This
685: * handles word styling.
686: */
1.86 kristaps 687: void
688: term_word(struct termp *p, const char *word)
1.65 kristaps 689: {
1.124 kristaps 690: const char *sv, *seq;
1.125 kristaps 691: int sz;
1.124 kristaps 692: size_t ssz;
693: enum roffdeco deco;
1.71 kristaps 694:
1.100 kristaps 695: sv = word;
696:
1.123 kristaps 697: if (word[0] && '\0' == word[1])
1.100 kristaps 698: switch (word[0]) {
699: case('.'):
700: /* FALLTHROUGH */
701: case(','):
702: /* FALLTHROUGH */
703: case(';'):
704: /* FALLTHROUGH */
705: case(':'):
706: /* FALLTHROUGH */
707: case('?'):
708: /* FALLTHROUGH */
709: case('!'):
710: /* FALLTHROUGH */
711: case(')'):
712: /* FALLTHROUGH */
713: case(']'):
714: if ( ! (TERMP_IGNDELIM & p->flags))
715: p->flags |= TERMP_NOSPACE;
716: break;
717: default:
718: break;
719: }
1.65 kristaps 720:
1.133 kristaps 721: if ( ! (TERMP_NOSPACE & p->flags)) {
1.125 kristaps 722: bufferc(p, ' ');
1.133 kristaps 723: if (TERMP_SENTENCE & p->flags)
724: bufferc(p, ' ');
725: }
1.65 kristaps 726:
1.71 kristaps 727: if ( ! (p->flags & TERMP_NONOSPACE))
728: p->flags &= ~TERMP_NOSPACE;
1.133 kristaps 729:
730: p->flags &= ~TERMP_SENTENCE;
1.65 kristaps 731:
1.125 kristaps 732: /* FIXME: use strcspn. */
1.124 kristaps 733:
734: while (*word) {
735: if ('\\' != *word) {
1.125 kristaps 736: encode(p, word, 1);
1.124 kristaps 737: word++;
738: continue;
739: }
740:
741: seq = ++word;
742: sz = a2roffdeco(&deco, &seq, &ssz);
743:
744: switch (deco) {
745: case (DECO_RESERVED):
1.125 kristaps 746: res(p, seq, ssz);
1.124 kristaps 747: break;
748: case (DECO_SPECIAL):
1.125 kristaps 749: spec(p, seq, ssz);
1.124 kristaps 750: break;
751: case (DECO_BOLD):
1.125 kristaps 752: term_fontrepl(p, TERMFONT_BOLD);
1.124 kristaps 753: break;
754: case (DECO_ITALIC):
1.125 kristaps 755: term_fontrepl(p, TERMFONT_UNDER);
1.124 kristaps 756: break;
757: case (DECO_ROMAN):
1.125 kristaps 758: term_fontrepl(p, TERMFONT_NONE);
1.124 kristaps 759: break;
760: case (DECO_PREVIOUS):
1.125 kristaps 761: term_fontlast(p);
1.124 kristaps 762: break;
763: default:
764: break;
765: }
1.127 kristaps 766:
1.124 kristaps 767: word += sz;
1.127 kristaps 768: if (DECO_NOSPACE == deco && '\0' == *word)
769: p->flags |= TERMP_NOSPACE;
1.124 kristaps 770: }
1.65 kristaps 771:
1.131 kristaps 772: /*
773: * Note that we don't process the pipe: the parser sees it as
774: * punctuation, but we don't in terms of typography.
775: */
1.100 kristaps 776: if (sv[0] && 0 == sv[1])
777: switch (sv[0]) {
778: case('('):
779: /* FALLTHROUGH */
780: case('['):
781: p->flags |= TERMP_NOSPACE;
782: break;
783: default:
784: break;
785: }
1.65 kristaps 786: }
787:
788:
1.71 kristaps 789: static void
1.125 kristaps 790: adjbuf(struct termp *p, size_t sz)
1.51 kristaps 791: {
792:
1.125 kristaps 793: if (0 == p->maxcols)
794: p->maxcols = 1024;
795: while (sz >= p->maxcols)
796: p->maxcols <<= 2;
797:
798: p->buf = realloc(p->buf, p->maxcols);
799: if (NULL == p->buf) {
800: perror(NULL);
801: exit(EXIT_FAILURE);
1.71 kristaps 802: }
1.51 kristaps 803: }
804:
1.79 kristaps 805:
806: static void
1.125 kristaps 807: buffera(struct termp *p, const char *word, size_t sz)
1.79 kristaps 808: {
1.125 kristaps 809:
810: if (p->col + sz >= p->maxcols)
811: adjbuf(p, p->col + sz);
812:
1.126 kristaps 813: memcpy(&p->buf[(int)p->col], word, sz);
1.125 kristaps 814: p->col += sz;
815: }
816:
817:
818: static void
819: bufferc(struct termp *p, char c)
820: {
821:
822: if (p->col + 1 >= p->maxcols)
823: adjbuf(p, p->col + 1);
824:
1.126 kristaps 825: p->buf[(int)p->col++] = c;
1.125 kristaps 826: }
827:
828:
829: static void
830: encode(struct termp *p, const char *word, size_t sz)
831: {
832: enum termfont f;
833: int i;
834:
835: /*
836: * Encode and buffer a string of characters. If the current
837: * font mode is unset, buffer directly, else encode then buffer
838: * character by character.
839: */
840:
1.142 kristaps 841: if (TERMTYPE_PS == p->type) {
842: buffera(p, word, sz);
843: return;
844: } else if (TERMFONT_NONE == (f = term_fonttop(p))) {
1.125 kristaps 845: buffera(p, word, sz);
846: return;
847: }
848:
849: for (i = 0; i < (int)sz; i++) {
850: if ( ! isgraph((u_char)word[i])) {
851: bufferc(p, word[i]);
852: continue;
1.79 kristaps 853: }
1.125 kristaps 854:
855: if (TERMFONT_UNDER == f)
856: bufferc(p, '_');
857: else
858: bufferc(p, word[i]);
859:
860: bufferc(p, 8);
861: bufferc(p, word[i]);
1.79 kristaps 862: }
863: }
1.106 kristaps 864:
865:
1.107 kristaps 866: size_t
867: term_vspan(const struct roffsu *su)
1.106 kristaps 868: {
869: double r;
870:
1.107 kristaps 871: switch (su->unit) {
1.106 kristaps 872: case (SCALE_CM):
1.107 kristaps 873: r = su->scale * 2;
1.106 kristaps 874: break;
875: case (SCALE_IN):
1.107 kristaps 876: r = su->scale * 6;
1.106 kristaps 877: break;
878: case (SCALE_PC):
1.107 kristaps 879: r = su->scale;
1.106 kristaps 880: break;
881: case (SCALE_PT):
1.107 kristaps 882: r = su->scale / 8;
1.106 kristaps 883: break;
884: case (SCALE_MM):
1.107 kristaps 885: r = su->scale / 1000;
1.106 kristaps 886: break;
887: case (SCALE_VS):
1.107 kristaps 888: r = su->scale;
1.106 kristaps 889: break;
890: default:
1.107 kristaps 891: r = su->scale - 1;
1.106 kristaps 892: break;
893: }
894:
895: if (r < 0.0)
896: r = 0.0;
1.107 kristaps 897: return(/* LINTED */(size_t)
1.106 kristaps 898: r);
899: }
900:
901:
1.107 kristaps 902: size_t
903: term_hspan(const struct roffsu *su)
1.106 kristaps 904: {
905: double r;
906:
1.108 kristaps 907: /* XXX: CM, IN, and PT are approximations. */
908:
1.107 kristaps 909: switch (su->unit) {
1.106 kristaps 910: case (SCALE_CM):
1.108 kristaps 911: r = 4 * su->scale;
1.106 kristaps 912: break;
913: case (SCALE_IN):
1.108 kristaps 914: /* XXX: this is an approximation. */
915: r = 10 * su->scale;
1.106 kristaps 916: break;
917: case (SCALE_PC):
1.108 kristaps 918: r = (10 * su->scale) / 6;
1.106 kristaps 919: break;
920: case (SCALE_PT):
1.108 kristaps 921: r = (10 * su->scale) / 72;
1.106 kristaps 922: break;
923: case (SCALE_MM):
1.107 kristaps 924: r = su->scale / 1000; /* FIXME: double-check. */
1.106 kristaps 925: break;
926: case (SCALE_VS):
1.107 kristaps 927: r = su->scale * 2 - 1; /* FIXME: double-check. */
1.106 kristaps 928: break;
929: default:
1.107 kristaps 930: r = su->scale;
1.106 kristaps 931: break;
932: }
933:
934: if (r < 0.0)
935: r = 0.0;
1.107 kristaps 936: return((size_t)/* LINTED */
1.106 kristaps 937: r);
938: }
939:
940:
CVSweb