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