]>
git.cameronkatri.com Git - mandoc.git/blob - term.c
1 /* $Id: term.c,v 1.6 2009/02/21 21:00:06 kristaps Exp $ */
3 * Copyright (c) 2008, 2009 Kristaps Dzonsons <kristaps@kth.se>
5 * Permission to use, copy, modify, and distribute this software for any
6 * purpose with or without fee is hereby granted, provided that the
7 * above copyright notice and this permission notice appear in all
10 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL
11 * WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED
12 * WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE
13 * AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL
14 * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR
15 * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER
16 * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
17 * PERFORMANCE OF THIS SOFTWARE.
35 static void termprint_r(struct termp
*,
36 const struct mdoc_meta
*,
37 const struct mdoc_node
*);
38 static void termprint_header(struct termp
*,
39 const struct mdoc_meta
*);
40 static void termprint_footer(struct termp
*,
41 const struct mdoc_meta
*);
43 static void pword(struct termp
*, const char *, size_t);
44 static void pescape(struct termp
*,
45 const char *, size_t *, size_t);
46 static void chara(struct termp
*, char);
47 static void style(struct termp
*, enum termstyle
);
51 flushln(struct termp
*p
)
53 size_t i
, j
, vsz
, vis
, maxvis
;
56 * First, establish the maximum columns of "visible" content.
57 * This is usually the difference between the right-margin and
58 * an indentation, but can be, for tagged lists or columns, a
59 * small set of values.
62 assert(p
->offset
< p
->rmargin
);
63 maxvis
= p
->rmargin
- p
->offset
;
67 * If in the standard case (left-justified), then begin with our
68 * indentation, otherwise (columns, etc.) just start spitting
72 if ( ! (p
->flags
& TERMP_NOLPAD
))
74 for (j
= 0; j
< p
->offset
; j
++)
77 for (i
= 0; i
< p
->col
; i
++) {
79 * Count up visible word characters. Control sequences
80 * (starting with the CSI) aren't counted.
82 assert( ! isspace(p
->buf
[i
]));
85 for (j
= i
, vsz
= 0; j
< p
->col
; j
++) {
86 if (isspace(p
->buf
[j
]))
88 else if (27 == p
->buf
[j
]) {
89 assert(j
+ 4 <= p
->col
);
97 * If a word is too long and we're within a line, put it
98 * on the next line. Puke if we're being asked to write
99 * something that will exceed the right margin (i.e.,
100 * from a fresh line or when we're not allowed to break
101 * the line with TERMP_NOBREAK).
104 if (vis
&& vis
+ vsz
>= maxvis
) {
106 if (p
->flags
& TERMP_NOBREAK
)
107 errx(1, "word breaks right margin");
109 for (j
= 0; j
< p
->offset
; j
++)
112 } else if (vis
+ vsz
>= maxvis
) {
114 errx(1, "word breaks right margin");
118 * Write out the word and a trailing space. Omit the
119 * space if we're the last word in the line.
122 for ( ; i
< p
->col
; i
++) {
123 if (isspace(p
->buf
[i
]))
135 * If we're not to right-marginalise it (newline), then instead
136 * pad to the right margin and stay off.
139 if (p
->flags
& TERMP_NOBREAK
) {
140 for ( ; vis
<= maxvis
; vis
++)
150 newln(struct termp
*p
)
154 * A newline only breaks an existing line; it won't assert
157 p
->flags
|= TERMP_NOSPACE
;
165 vspace(struct termp
*p
)
169 * Asserts a vertical space (a full, empty line-break between
178 chara(struct termp
*p
, char c
)
181 /* TODO: dynamically expand the buffer. */
182 if (p
->col
+ 1 >= p
->maxcols
)
183 errx(1, "line overrun");
184 p
->buf
[(p
->col
)++] = c
;
189 style(struct termp
*p
, enum termstyle esc
)
192 if (p
->col
+ 4 >= p
->maxcols
)
193 errx(1, "line overrun");
195 p
->buf
[(p
->col
)++] = 27;
196 p
->buf
[(p
->col
)++] = '[';
199 p
->buf
[(p
->col
)++] = '0';
202 p
->buf
[(p
->col
)++] = '1';
204 case (STYLE_UNDERLINE
):
205 p
->buf
[(p
->col
)++] = '4';
211 p
->buf
[(p
->col
)++] = 'm';
216 pescape(struct termp
*p
, const char *word
, size_t *i
, size_t len
)
222 if ('(' == word
[*i
]) {
223 /* Two-character escapes. */
225 assert(*i
+ 1 < len
);
227 if ('r' == word
[*i
] && 'B' == word
[*i
+ 1])
229 else if ('l' == word
[*i
] && 'B' == word
[*i
+ 1])
235 } else if ('[' != word
[*i
]) {
236 /* One-character escapes. */
253 /* n-character escapes. */
258 pword(struct termp
*p
, const char *word
, size_t len
)
264 if ( ! (p
->flags
& TERMP_NOSPACE
))
267 p
->flags
&= ~TERMP_NOSPACE
;
269 if (p
->flags
& TERMP_BOLD
)
270 style(p
, STYLE_BOLD
);
271 if (p
->flags
& TERMP_UNDERLINE
)
272 style(p
, STYLE_UNDERLINE
);
274 for (i
= 0; i
< len
; i
++) {
275 if ('\\' == word
[i
]) {
276 pescape(p
, word
, &i
, len
);
282 if (p
->flags
& TERMP_BOLD
||
283 p
->flags
& TERMP_UNDERLINE
)
284 style(p
, STYLE_CLEAR
);
289 word(struct termp
*p
, const char *word
)
293 if (mdoc_isdelim(word
))
294 p
->flags
|= TERMP_NOSPACE
;
300 for (j
= i
= 0; i
< len
; i
++) {
301 if ( ! isspace(word
[i
])) {
308 pword(p
, &word
[i
- j
], j
);
313 pword(p
, &word
[i
- j
], j
);
319 termprint_r(struct termp
*p
, const struct mdoc_meta
*meta
,
320 const struct mdoc_node
*node
)
324 /* Pre-processing. */
328 if (MDOC_TEXT
!= node
->type
) {
329 if (termacts
[node
->tok
].pre
)
330 if ( ! (*termacts
[node
->tok
].pre
)(p
, meta
, node
))
332 } else /* MDOC_TEXT == node->type */
333 word(p
, node
->data
.text
.string
);
337 if (dochild
&& node
->child
)
338 termprint_r(p
, meta
, node
->child
);
340 /* Post-processing. */
342 if (MDOC_TEXT
!= node
->type
)
343 if (termacts
[node
->tok
].post
)
344 (*termacts
[node
->tok
].post
)(p
, meta
, node
);
349 termprint_r(p
, meta
, node
->next
);
354 termprint_footer(struct termp
*p
, const struct mdoc_meta
*meta
)
358 size_t sz
, osz
, ssz
, i
;
360 if (NULL
== (buf
= malloc(p
->rmargin
)))
362 if (NULL
== (os
= malloc(p
->rmargin
)))
365 tm
= localtime(&meta
->date
);
366 if (NULL
== strftime(buf
, p
->rmargin
, "%B %d, %Y", tm
))
369 osz
= strlcpy(os
, meta
->os
, p
->rmargin
);
374 if (ssz
> p
->rmargin
) {
380 ssz
= p
->rmargin
- ssz
+ 1;
384 for (i
= 0; i
< ssz
; i
++)
396 termprint_header(struct termp
*p
, const struct mdoc_meta
*meta
)
399 const char *pp
, *msec
;
400 size_t ssz
, tsz
, ttsz
, i
;;
402 if (NULL
== (buf
= malloc(p
->rmargin
)))
404 if (NULL
== (title
= malloc(p
->rmargin
)))
407 if (NULL
== (pp
= mdoc_vol2a(meta
->vol
)))
408 switch (meta
->msec
) {
414 pp
= mdoc_vol2a(VOL_URM
);
417 pp
= mdoc_vol2a(VOL_SMM
);
426 pp
= mdoc_vol2a(VOL_PRM
);
429 pp
= mdoc_vol2a(VOL_KM
);
432 /* FIXME: capitalise. */
433 if (NULL
== (pp
= mdoc_msec2a(meta
->msec
)))
434 pp
= mdoc_msec2a(MSEC_local
);
439 tsz
= strlcpy(buf
, pp
, p
->rmargin
);
440 assert(tsz
< p
->rmargin
);
442 if ((pp
= mdoc_arch2a(meta
->arch
))) {
443 tsz
= strlcat(buf
, " (", p
->rmargin
);
444 assert(tsz
< p
->rmargin
);
445 tsz
= strlcat(buf
, pp
, p
->rmargin
);
446 assert(tsz
< p
->rmargin
);
447 tsz
= strlcat(buf
, ")", p
->rmargin
);
448 assert(tsz
< p
->rmargin
);
451 ttsz
= strlcpy(title
, meta
->title
, p
->rmargin
);
453 if (NULL
== (msec
= mdoc_msec2a(meta
->msec
)))
456 ssz
= (2 * (ttsz
+ 2 + strlen(msec
))) + tsz
+ 2;
458 if (ssz
> p
->rmargin
) {
459 if ((ssz
-= p
->rmargin
) % 2)
464 title
[ttsz
- ssz
] = 0;
467 ssz
= ((p
->rmargin
- ssz
) / 2) + 1;
469 printf("%s(%s)", title
, msec
);
471 for (i
= 0; i
< ssz
; i
++)
476 for (i
= 0; i
< ssz
; i
++)
479 printf("%s(%s)\n", title
, msec
);
488 termprint(const struct mdoc_node
*node
,
489 const struct mdoc_meta
*meta
)
493 p
.maxrmargin
= 80; /* XXX */
494 p
.rmargin
= p
.maxrmargin
;
496 p
.offset
= p
.col
= 0;
497 p
.flags
= TERMP_NOSPACE
;
499 if (NULL
== (p
.buf
= malloc(p
.maxcols
)))
502 termprint_header(&p
, meta
);
503 termprint_r(&p
, meta
, node
);
504 termprint_footer(&p
, meta
);