]>
git.cameronkatri.com Git - mandoc.git/blob - mdocterm.c
1 /* $Id: mdocterm.c,v 1.9 2009/02/25 12:09:20 kristaps Exp $ */
3 * Copyright (c) 2008 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 #define xisspace(x) isspace((int)(x))
37 #define xisspace(x) isspace((x))
46 static void body(struct termp
*,
47 const struct mdoc_meta
*,
48 const struct mdoc_node
*);
49 static void header(struct termp
*,
50 const struct mdoc_meta
*);
51 static void footer(struct termp
*,
52 const struct mdoc_meta
*);
54 static void pword(struct termp
*, const char *, size_t);
55 static void pescape(struct termp
*,
56 const char *, size_t *, size_t);
57 static void chara(struct termp
*, char);
58 static void stringa(struct termp
*, const char *);
59 static void style(struct termp
*, enum termstyle
);
62 extern size_t strlcat(char *, const char *, size_t);
63 extern size_t strlcpy(char *, const char *, size_t);
68 main(int argc
, char *argv
[])
71 const struct mdoc
*mdoc
;
76 if ( ! mmain_getopt(p
, argc
, argv
, NULL
, NULL
, NULL
, NULL
))
79 if (NULL
== (mdoc
= mmain_mdoc(p
)))
82 termp
.maxrmargin
= 80; /* XXX */
83 termp
.rmargin
= termp
.maxrmargin
;
85 termp
.offset
= termp
.col
= 0;
86 termp
.flags
= TERMP_NOSPACE
;
88 if (NULL
== (termp
.buf
= malloc(termp
.maxcols
)))
91 header(&termp
, mdoc_meta(mdoc
));
92 body(&termp
, mdoc_meta(mdoc
), mdoc_node(mdoc
));
93 footer(&termp
, mdoc_meta(mdoc
));
103 flushln(struct termp
*p
)
105 size_t i
, j
, vsz
, vis
, maxvis
;
108 * First, establish the maximum columns of "visible" content.
109 * This is usually the difference between the right-margin and
110 * an indentation, but can be, for tagged lists or columns, a
111 * small set of values.
114 assert(p
->offset
< p
->rmargin
);
115 maxvis
= p
->rmargin
- p
->offset
;
119 * If in the standard case (left-justified), then begin with our
120 * indentation, otherwise (columns, etc.) just start spitting
124 if ( ! (p
->flags
& TERMP_NOLPAD
))
126 for (j
= 0; j
< p
->offset
; j
++)
130 * If we're literal, print out verbatim.
132 if (p
->flags
& TERMP_LITERAL
) {
133 /* FIXME: count non-printing chars. */
134 for (i
= 0; i
< p
->col
; i
++)
141 for (i
= 0; i
< p
->col
; i
++) {
143 * Count up visible word characters. Control sequences
144 * (starting with the CSI) aren't counted.
146 assert( ! xisspace(p
->buf
[i
]));
149 for (j
= i
, vsz
= 0; j
< p
->col
; j
++) {
150 if (xisspace(p
->buf
[j
]))
152 else if (27 == p
->buf
[j
]) {
153 assert(j
+ 4 <= p
->col
);
161 * If a word is too long and we're within a line, put it
162 * on the next line. Puke if we're being asked to write
163 * something that will exceed the right margin (i.e.,
164 * from a fresh line or when we're not allowed to break
165 * the line with TERMP_NOBREAK).
168 if (vis
&& vis
+ vsz
>= maxvis
) {
170 if (p
->flags
& TERMP_NOBREAK
)
171 errx(1, "word breaks right margin");
173 for (j
= 0; j
< p
->offset
; j
++)
176 } else if (vis
+ vsz
>= maxvis
)
178 errx(1, "word breaks right margin");
181 * Write out the word and a trailing space. Omit the
182 * space if we're the last word in the line.
185 for ( ; i
< p
->col
; i
++) {
186 if (xisspace(p
->buf
[i
]))
198 * If we're not to right-marginalise it (newline), then instead
199 * pad to the right margin and stay off.
202 if (p
->flags
& TERMP_NOBREAK
) {
203 for ( ; vis
<= maxvis
; vis
++)
213 newln(struct termp
*p
)
217 * A newline only breaks an existing line; it won't assert
220 p
->flags
|= TERMP_NOSPACE
;
228 vspace(struct termp
*p
)
232 * Asserts a vertical space (a full, empty line-break between
241 stringa(struct termp
*p
, const char *s
)
244 /* XXX - speed up if not passing to chara. */
251 chara(struct termp
*p
, char c
)
254 /* TODO: dynamically expand the buffer. */
255 if (p
->col
+ 1 >= p
->maxcols
)
256 errx(1, "line overrun");
257 p
->buf
[(p
->col
)++] = c
;
262 style(struct termp
*p
, enum termstyle esc
)
265 if (p
->col
+ 4 >= p
->maxcols
)
266 errx(1, "line overrun");
268 p
->buf
[(p
->col
)++] = 27;
269 p
->buf
[(p
->col
)++] = '[';
272 p
->buf
[(p
->col
)++] = '0';
275 p
->buf
[(p
->col
)++] = '1';
277 case (STYLE_UNDERLINE
):
278 p
->buf
[(p
->col
)++] = '4';
284 p
->buf
[(p
->col
)++] = 'm';
289 pescape(struct termp
*p
, const char *word
, size_t *i
, size_t len
)
295 if ('(' == word
[*i
]) {
296 /* Two-character escapes. */
298 assert(*i
+ 1 < len
);
300 if ('r' == word
[*i
] && 'B' == word
[*i
+ 1])
302 else if ('l' == word
[*i
] && 'B' == word
[*i
+ 1])
304 else if ('<' == word
[*i
] && '-' == word
[*i
+ 1])
306 else if ('-' == word
[*i
] && '>' == word
[*i
+ 1])
308 else if ('l' == word
[*i
] && 'q' == word
[*i
+ 1])
310 else if ('r' == word
[*i
] && 'q' == word
[*i
+ 1])
316 } else if ('[' != word
[*i
]) {
317 /* One-character escapes. */
334 /* n-character escapes. */
339 pword(struct termp
*p
, const char *word
, size_t len
)
343 /*assert(len > 0);*/ /* Can be, if literal. */
345 if ( ! (p
->flags
& TERMP_NOSPACE
) &&
346 ! (p
->flags
& TERMP_LITERAL
))
349 p
->flags
&= ~TERMP_NOSPACE
;
351 if (p
->flags
& TERMP_BOLD
)
352 style(p
, STYLE_BOLD
);
353 if (p
->flags
& TERMP_UNDERLINE
)
354 style(p
, STYLE_UNDERLINE
);
356 for (i
= 0; i
< len
; i
++) {
357 if ('\\' == word
[i
]) {
358 pescape(p
, word
, &i
, len
);
364 if (p
->flags
& TERMP_BOLD
||
365 p
->flags
& TERMP_UNDERLINE
)
366 style(p
, STYLE_CLEAR
);
371 word(struct termp
*p
, const char *word
)
375 if (p
->flags
& TERMP_LITERAL
) {
376 pword(p
, word
, strlen(word
));
383 if (mdoc_isdelim(word
)) {
384 if ( ! (p
->flags
& TERMP_IGNDELIM
))
385 p
->flags
|= TERMP_NOSPACE
;
386 p
->flags
&= ~TERMP_IGNDELIM
;
390 for (j
= i
= 0; i
< len
; i
++) {
391 if ( ! xisspace(word
[i
])) {
398 pword(p
, &word
[i
- j
], j
);
403 pword(p
, &word
[i
- j
], j
);
409 body(struct termp
*p
, const struct mdoc_meta
*meta
,
410 const struct mdoc_node
*node
)
413 struct termpair pair
;
415 /* Pre-processing. */
420 if (MDOC_TEXT
!= node
->type
) {
421 if (termacts
[node
->tok
].pre
)
422 if ( ! (*termacts
[node
->tok
].pre
)(p
, &pair
, meta
, node
))
424 } else /* MDOC_TEXT == node->type */
425 word(p
, node
->data
.text
.string
);
430 case (TERMPAIR_FLAG
):
431 p
->flags
|= pair
.data
.flag
;
437 if (dochild
&& node
->child
)
438 body(p
, meta
, node
->child
);
441 case (TERMPAIR_FLAG
):
442 p
->flags
&= ~pair
.data
.flag
;
448 /* Post-processing. */
450 if (MDOC_TEXT
!= node
->type
)
451 if (termacts
[node
->tok
].post
)
452 (*termacts
[node
->tok
].post
)(p
, &pair
, meta
, node
);
457 body(p
, meta
, node
->next
);
462 footer(struct termp
*p
, const struct mdoc_meta
*meta
)
466 size_t sz
, osz
, ssz
, i
;
468 if (NULL
== (buf
= malloc(p
->rmargin
)))
470 if (NULL
== (os
= malloc(p
->rmargin
)))
473 tm
= localtime(&meta
->date
);
476 if (NULL
== strftime(buf
, p
->rmargin
, "%B %d, %Y", tm
))
478 if (0 == strftime(buf
, p
->rmargin
, "%B %d, %Y", tm
))
482 osz
= strlcpy(os
, meta
->os
, p
->rmargin
);
487 if (ssz
> p
->rmargin
) {
493 ssz
= p
->rmargin
- ssz
+ 1;
497 for (i
= 0; i
< ssz
; i
++)
509 header(struct termp
*p
, const struct mdoc_meta
*meta
)
512 const char *pp
, *msec
;
513 size_t ssz
, tsz
, ttsz
, i
;;
515 if (NULL
== (buf
= malloc(p
->rmargin
)))
517 if (NULL
== (title
= malloc(p
->rmargin
)))
520 if (NULL
== (pp
= mdoc_vol2a(meta
->vol
)))
521 switch (meta
->msec
) {
527 pp
= mdoc_vol2a(VOL_URM
);
530 pp
= mdoc_vol2a(VOL_SMM
);
539 pp
= mdoc_vol2a(VOL_PRM
);
542 pp
= mdoc_vol2a(VOL_KM
);
545 /* FIXME: capitalise. */
546 if (NULL
== (pp
= mdoc_msec2a(meta
->msec
)))
547 pp
= mdoc_msec2a(MSEC_local
);
552 tsz
= strlcpy(buf
, pp
, p
->rmargin
);
553 assert(tsz
< p
->rmargin
);
555 if ((pp
= mdoc_arch2a(meta
->arch
))) {
556 tsz
= strlcat(buf
, " (", p
->rmargin
);
557 assert(tsz
< p
->rmargin
);
558 tsz
= strlcat(buf
, pp
, p
->rmargin
);
559 assert(tsz
< p
->rmargin
);
560 tsz
= strlcat(buf
, ")", p
->rmargin
);
561 assert(tsz
< p
->rmargin
);
564 ttsz
= strlcpy(title
, meta
->title
, p
->rmargin
);
566 if (NULL
== (msec
= mdoc_msec2a(meta
->msec
)))
569 ssz
= (2 * (ttsz
+ 2 + strlen(msec
))) + tsz
+ 2;
571 if (ssz
> p
->rmargin
) {
572 if ((ssz
-= p
->rmargin
) % 2)
577 title
[ttsz
- ssz
] = 0;
580 ssz
= ((p
->rmargin
- ssz
) / 2) + 1;
582 printf("%s(%s)", title
, msec
);
584 for (i
= 0; i
< ssz
; i
++)
589 for (i
= 0; i
< ssz
; i
++)
592 printf("%s(%s)\n", title
, msec
);