]>
git.cameronkatri.com Git - mandoc.git/blob - term.c
1 /* $Id: term.c,v 1.9 2009/02/22 19:23:48 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.
39 static void termprint_r(struct termp
*,
40 const struct mdoc_meta
*,
41 const struct mdoc_node
*);
42 static void termprint_header(struct termp
*,
43 const struct mdoc_meta
*);
44 static void termprint_footer(struct termp
*,
45 const struct mdoc_meta
*);
47 static void pword(struct termp
*, const char *, size_t);
48 static void pescape(struct termp
*,
49 const char *, size_t *, size_t);
50 static void chara(struct termp
*, char);
51 static void style(struct termp
*, enum termstyle
);
54 extern size_t strlcat(char *, const char *, size_t);
55 extern size_t strlcpy(char *, const char *, size_t);
59 flushln(struct termp
*p
)
61 size_t i
, j
, vsz
, vis
, maxvis
;
64 * First, establish the maximum columns of "visible" content.
65 * This is usually the difference between the right-margin and
66 * an indentation, but can be, for tagged lists or columns, a
67 * small set of values.
70 assert(p
->offset
< p
->rmargin
);
71 maxvis
= p
->rmargin
- p
->offset
;
75 * If in the standard case (left-justified), then begin with our
76 * indentation, otherwise (columns, etc.) just start spitting
80 if ( ! (p
->flags
& TERMP_NOLPAD
))
82 for (j
= 0; j
< p
->offset
; j
++)
86 * If we're literal, print out verbatim.
88 if (p
->flags
& TERMP_LITERAL
) {
89 /* FIXME: count non-printing chars. */
90 for (i
= 0; i
< p
->col
; i
++)
97 for (i
= 0; i
< p
->col
; i
++) {
99 * Count up visible word characters. Control sequences
100 * (starting with the CSI) aren't counted.
102 assert( ! isspace(p
->buf
[i
]));
105 for (j
= i
, vsz
= 0; j
< p
->col
; j
++) {
106 if (isspace(p
->buf
[j
]))
108 else if (27 == p
->buf
[j
]) {
109 assert(j
+ 4 <= p
->col
);
117 * If a word is too long and we're within a line, put it
118 * on the next line. Puke if we're being asked to write
119 * something that will exceed the right margin (i.e.,
120 * from a fresh line or when we're not allowed to break
121 * the line with TERMP_NOBREAK).
124 if (vis
&& vis
+ vsz
>= maxvis
) {
126 if (p
->flags
& TERMP_NOBREAK
)
127 errx(1, "word breaks right margin");
129 for (j
= 0; j
< p
->offset
; j
++)
132 } else if (vis
+ vsz
>= maxvis
) {
134 errx(1, "word breaks right margin");
138 * Write out the word and a trailing space. Omit the
139 * space if we're the last word in the line.
142 for ( ; i
< p
->col
; i
++) {
143 if (isspace(p
->buf
[i
]))
155 * If we're not to right-marginalise it (newline), then instead
156 * pad to the right margin and stay off.
159 if (p
->flags
& TERMP_NOBREAK
) {
160 for ( ; vis
<= maxvis
; vis
++)
170 newln(struct termp
*p
)
174 * A newline only breaks an existing line; it won't assert
177 p
->flags
|= TERMP_NOSPACE
;
185 vspace(struct termp
*p
)
189 * Asserts a vertical space (a full, empty line-break between
198 chara(struct termp
*p
, char c
)
201 /* TODO: dynamically expand the buffer. */
202 if (p
->col
+ 1 >= p
->maxcols
)
203 errx(1, "line overrun");
204 p
->buf
[(p
->col
)++] = c
;
209 style(struct termp
*p
, enum termstyle esc
)
212 if (p
->col
+ 4 >= p
->maxcols
)
213 errx(1, "line overrun");
215 p
->buf
[(p
->col
)++] = 27;
216 p
->buf
[(p
->col
)++] = '[';
219 p
->buf
[(p
->col
)++] = '0';
222 p
->buf
[(p
->col
)++] = '1';
224 case (STYLE_UNDERLINE
):
225 p
->buf
[(p
->col
)++] = '4';
231 p
->buf
[(p
->col
)++] = 'm';
236 pescape(struct termp
*p
, const char *word
, size_t *i
, size_t len
)
242 if ('(' == word
[*i
]) {
243 /* Two-character escapes. */
245 assert(*i
+ 1 < len
);
247 if ('r' == word
[*i
] && 'B' == word
[*i
+ 1])
249 else if ('l' == word
[*i
] && 'B' == word
[*i
+ 1])
255 } else if ('[' != word
[*i
]) {
256 /* One-character escapes. */
273 /* n-character escapes. */
278 pword(struct termp
*p
, const char *word
, size_t len
)
282 /*assert(len > 0);*/ /* Can be, if literal. */
284 if ( ! (p
->flags
& TERMP_NOSPACE
) &&
285 ! (p
->flags
& TERMP_LITERAL
))
288 p
->flags
&= ~TERMP_NOSPACE
;
290 if (p
->flags
& TERMP_BOLD
)
291 style(p
, STYLE_BOLD
);
292 if (p
->flags
& TERMP_UNDERLINE
)
293 style(p
, STYLE_UNDERLINE
);
295 for (i
= 0; i
< len
; i
++) {
296 if ('\\' == word
[i
]) {
297 pescape(p
, word
, &i
, len
);
303 if (p
->flags
& TERMP_BOLD
||
304 p
->flags
& TERMP_UNDERLINE
)
305 style(p
, STYLE_CLEAR
);
310 word(struct termp
*p
, const char *word
)
314 if (p
->flags
& TERMP_LITERAL
) {
315 pword(p
, word
, strlen(word
));
322 if (mdoc_isdelim(word
)) {
323 if ( ! (p
->flags
& TERMP_IGNDELIM
))
324 p
->flags
|= TERMP_NOSPACE
;
325 p
->flags
&= ~TERMP_IGNDELIM
;
329 for (j
= i
= 0; i
< len
; i
++) {
330 if ( ! isspace(word
[i
])) {
337 pword(p
, &word
[i
- j
], j
);
342 pword(p
, &word
[i
- j
], j
);
348 termprint_r(struct termp
*p
, const struct mdoc_meta
*meta
,
349 const struct mdoc_node
*node
)
353 /* Pre-processing. */
357 if (MDOC_TEXT
!= node
->type
) {
358 if (termacts
[node
->tok
].pre
)
359 if ( ! (*termacts
[node
->tok
].pre
)(p
, meta
, node
))
361 } else /* MDOC_TEXT == node->type */
362 word(p
, node
->data
.text
.string
);
366 if (dochild
&& node
->child
)
367 termprint_r(p
, meta
, node
->child
);
369 /* Post-processing. */
371 if (MDOC_TEXT
!= node
->type
)
372 if (termacts
[node
->tok
].post
)
373 (*termacts
[node
->tok
].post
)(p
, meta
, node
);
378 termprint_r(p
, meta
, node
->next
);
383 termprint_footer(struct termp
*p
, const struct mdoc_meta
*meta
)
387 size_t sz
, osz
, ssz
, i
;
389 if (NULL
== (buf
= malloc(p
->rmargin
)))
391 if (NULL
== (os
= malloc(p
->rmargin
)))
394 tm
= localtime(&meta
->date
);
397 if (0 == strftime(buf
, p
->rmargin
, "%B %d, %Y", tm
))
399 if (NULL
== strftime(buf
, p
->rmargin
, "%B %d, %Y", tm
))
403 osz
= strlcpy(os
, meta
->os
, p
->rmargin
);
408 if (ssz
> p
->rmargin
) {
414 ssz
= p
->rmargin
- ssz
+ 1;
418 for (i
= 0; i
< ssz
; i
++)
430 termprint_header(struct termp
*p
, const struct mdoc_meta
*meta
)
433 const char *pp
, *msec
;
434 size_t ssz
, tsz
, ttsz
, i
;;
436 if (NULL
== (buf
= malloc(p
->rmargin
)))
438 if (NULL
== (title
= malloc(p
->rmargin
)))
441 if (NULL
== (pp
= mdoc_vol2a(meta
->vol
)))
442 switch (meta
->msec
) {
448 pp
= mdoc_vol2a(VOL_URM
);
451 pp
= mdoc_vol2a(VOL_SMM
);
460 pp
= mdoc_vol2a(VOL_PRM
);
463 pp
= mdoc_vol2a(VOL_KM
);
466 /* FIXME: capitalise. */
467 if (NULL
== (pp
= mdoc_msec2a(meta
->msec
)))
468 pp
= mdoc_msec2a(MSEC_local
);
473 tsz
= strlcpy(buf
, pp
, p
->rmargin
);
474 assert(tsz
< p
->rmargin
);
476 if ((pp
= mdoc_arch2a(meta
->arch
))) {
477 tsz
= strlcat(buf
, " (", p
->rmargin
);
478 assert(tsz
< p
->rmargin
);
479 tsz
= strlcat(buf
, pp
, p
->rmargin
);
480 assert(tsz
< p
->rmargin
);
481 tsz
= strlcat(buf
, ")", p
->rmargin
);
482 assert(tsz
< p
->rmargin
);
485 ttsz
= strlcpy(title
, meta
->title
, p
->rmargin
);
487 if (NULL
== (msec
= mdoc_msec2a(meta
->msec
)))
490 ssz
= (2 * (ttsz
+ 2 + strlen(msec
))) + tsz
+ 2;
492 if (ssz
> p
->rmargin
) {
493 if ((ssz
-= p
->rmargin
) % 2)
498 title
[ttsz
- ssz
] = 0;
501 ssz
= ((p
->rmargin
- ssz
) / 2) + 1;
503 printf("%s(%s)", title
, msec
);
505 for (i
= 0; i
< ssz
; i
++)
510 for (i
= 0; i
< ssz
; i
++)
513 printf("%s(%s)\n", title
, msec
);
522 termprint(const struct mdoc_node
*node
,
523 const struct mdoc_meta
*meta
)
527 p
.maxrmargin
= 80; /* XXX */
528 p
.rmargin
= p
.maxrmargin
;
530 p
.offset
= p
.col
= 0;
531 p
.flags
= TERMP_NOSPACE
;
533 if (NULL
== (p
.buf
= malloc(p
.maxcols
)))
536 termprint_header(&p
, meta
);
537 termprint_r(&p
, meta
, node
);
538 termprint_footer(&p
, meta
);