]>
git.cameronkatri.com Git - mandoc.git/blob - mdocterm.c
1 /* $Id: mdocterm.c,v 1.5 2009/02/23 12:45:19 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.
40 static void body(struct termp
*,
41 const struct mdoc_meta
*,
42 const struct mdoc_node
*);
43 static void header(struct termp
*,
44 const struct mdoc_meta
*);
45 static void footer(struct termp
*,
46 const struct mdoc_meta
*);
48 static void pword(struct termp
*, const char *, size_t);
49 static void pescape(struct termp
*,
50 const char *, size_t *, size_t);
51 static void chara(struct termp
*, char);
52 static void stringa(struct termp
*, const char *);
53 static void style(struct termp
*, enum termstyle
);
56 extern size_t strlcat(char *, const char *, size_t);
57 extern size_t strlcpy(char *, const char *, size_t);
62 main(int argc
, char *argv
[])
65 const struct mdoc
*mdoc
;
70 if ( ! mmain_getopt(p
, argc
, argv
, NULL
, NULL
, NULL
, NULL
))
73 if (NULL
== (mdoc
= mmain_mdoc(p
)))
76 termp
.maxrmargin
= 80; /* XXX */
77 termp
.rmargin
= termp
.maxrmargin
;
79 termp
.offset
= termp
.col
= 0;
80 termp
.flags
= TERMP_NOSPACE
;
82 if (NULL
== (termp
.buf
= malloc(termp
.maxcols
)))
85 header(&termp
, mdoc_meta(mdoc
));
86 body(&termp
, mdoc_meta(mdoc
), mdoc_node(mdoc
));
87 footer(&termp
, mdoc_meta(mdoc
));
97 flushln(struct termp
*p
)
99 size_t i
, j
, vsz
, vis
, maxvis
;
102 * First, establish the maximum columns of "visible" content.
103 * This is usually the difference between the right-margin and
104 * an indentation, but can be, for tagged lists or columns, a
105 * small set of values.
108 assert(p
->offset
< p
->rmargin
);
109 maxvis
= p
->rmargin
- p
->offset
;
113 * If in the standard case (left-justified), then begin with our
114 * indentation, otherwise (columns, etc.) just start spitting
118 if ( ! (p
->flags
& TERMP_NOLPAD
))
120 for (j
= 0; j
< p
->offset
; j
++)
124 * If we're literal, print out verbatim.
126 if (p
->flags
& TERMP_LITERAL
) {
127 /* FIXME: count non-printing chars. */
128 for (i
= 0; i
< p
->col
; i
++)
135 for (i
= 0; i
< p
->col
; i
++) {
137 * Count up visible word characters. Control sequences
138 * (starting with the CSI) aren't counted.
140 assert( ! isspace(p
->buf
[i
]));
143 for (j
= i
, vsz
= 0; j
< p
->col
; j
++) {
144 if (isspace(p
->buf
[j
]))
146 else if (27 == p
->buf
[j
]) {
147 assert(j
+ 4 <= p
->col
);
155 * If a word is too long and we're within a line, put it
156 * on the next line. Puke if we're being asked to write
157 * something that will exceed the right margin (i.e.,
158 * from a fresh line or when we're not allowed to break
159 * the line with TERMP_NOBREAK).
162 if (vis
&& vis
+ vsz
>= maxvis
) {
164 if (p
->flags
& TERMP_NOBREAK
)
165 errx(1, "word breaks right margin");
167 for (j
= 0; j
< p
->offset
; j
++)
170 } else if (vis
+ vsz
>= maxvis
) {
172 errx(1, "word breaks right margin");
176 * Write out the word and a trailing space. Omit the
177 * space if we're the last word in the line.
180 for ( ; i
< p
->col
; i
++) {
181 if (isspace(p
->buf
[i
]))
193 * If we're not to right-marginalise it (newline), then instead
194 * pad to the right margin and stay off.
197 if (p
->flags
& TERMP_NOBREAK
) {
198 for ( ; vis
<= maxvis
; vis
++)
208 newln(struct termp
*p
)
212 * A newline only breaks an existing line; it won't assert
215 p
->flags
|= TERMP_NOSPACE
;
223 vspace(struct termp
*p
)
227 * Asserts a vertical space (a full, empty line-break between
236 stringa(struct termp
*p
, const char *s
)
239 /* XXX - speed up if not passing to chara. */
246 chara(struct termp
*p
, char c
)
249 /* TODO: dynamically expand the buffer. */
250 if (p
->col
+ 1 >= p
->maxcols
)
251 errx(1, "line overrun");
252 p
->buf
[(p
->col
)++] = c
;
257 style(struct termp
*p
, enum termstyle esc
)
260 if (p
->col
+ 4 >= p
->maxcols
)
261 errx(1, "line overrun");
263 p
->buf
[(p
->col
)++] = 27;
264 p
->buf
[(p
->col
)++] = '[';
267 p
->buf
[(p
->col
)++] = '0';
270 p
->buf
[(p
->col
)++] = '1';
272 case (STYLE_UNDERLINE
):
273 p
->buf
[(p
->col
)++] = '4';
279 p
->buf
[(p
->col
)++] = 'm';
284 pescape(struct termp
*p
, const char *word
, size_t *i
, size_t len
)
290 if ('(' == word
[*i
]) {
291 /* Two-character escapes. */
293 assert(*i
+ 1 < len
);
295 if ('r' == word
[*i
] && 'B' == word
[*i
+ 1])
297 else if ('l' == word
[*i
] && 'B' == word
[*i
+ 1])
299 else if ('<' == word
[*i
] && '-' == word
[*i
+ 1])
301 else if ('-' == word
[*i
] && '>' == word
[*i
+ 1])
307 } else if ('[' != word
[*i
]) {
308 /* One-character escapes. */
325 /* n-character escapes. */
330 pword(struct termp
*p
, const char *word
, size_t len
)
334 /*assert(len > 0);*/ /* Can be, if literal. */
336 if ( ! (p
->flags
& TERMP_NOSPACE
) &&
337 ! (p
->flags
& TERMP_LITERAL
))
340 p
->flags
&= ~TERMP_NOSPACE
;
342 if (p
->flags
& TERMP_BOLD
)
343 style(p
, STYLE_BOLD
);
344 if (p
->flags
& TERMP_UNDERLINE
)
345 style(p
, STYLE_UNDERLINE
);
347 for (i
= 0; i
< len
; i
++) {
348 if ('\\' == word
[i
]) {
349 pescape(p
, word
, &i
, len
);
355 if (p
->flags
& TERMP_BOLD
||
356 p
->flags
& TERMP_UNDERLINE
)
357 style(p
, STYLE_CLEAR
);
362 word(struct termp
*p
, const char *word
)
366 if (p
->flags
& TERMP_LITERAL
) {
367 pword(p
, word
, strlen(word
));
374 if (mdoc_isdelim(word
)) {
375 if ( ! (p
->flags
& TERMP_IGNDELIM
))
376 p
->flags
|= TERMP_NOSPACE
;
377 p
->flags
&= ~TERMP_IGNDELIM
;
381 for (j
= i
= 0; i
< len
; i
++) {
382 if ( ! isspace(word
[i
])) {
389 pword(p
, &word
[i
- j
], j
);
394 pword(p
, &word
[i
- j
], j
);
400 body(struct termp
*p
, const struct mdoc_meta
*meta
,
401 const struct mdoc_node
*node
)
405 /* Pre-processing. */
409 if (MDOC_TEXT
!= node
->type
) {
410 if (termacts
[node
->tok
].pre
)
411 if ( ! (*termacts
[node
->tok
].pre
)(p
, meta
, node
))
413 } else /* MDOC_TEXT == node->type */
414 word(p
, node
->data
.text
.string
);
418 if (dochild
&& node
->child
)
419 body(p
, meta
, node
->child
);
421 /* Post-processing. */
423 if (MDOC_TEXT
!= node
->type
)
424 if (termacts
[node
->tok
].post
)
425 (*termacts
[node
->tok
].post
)(p
, meta
, node
);
430 body(p
, meta
, node
->next
);
435 footer(struct termp
*p
, const struct mdoc_meta
*meta
)
439 size_t sz
, osz
, ssz
, i
;
441 if (NULL
== (buf
= malloc(p
->rmargin
)))
443 if (NULL
== (os
= malloc(p
->rmargin
)))
446 tm
= localtime(&meta
->date
);
449 if (0 == strftime(buf
, p
->rmargin
, "%B %d, %Y", tm
))
451 if (NULL
== strftime(buf
, p
->rmargin
, "%B %d, %Y", tm
))
455 osz
= strlcpy(os
, meta
->os
, p
->rmargin
);
460 if (ssz
> p
->rmargin
) {
466 ssz
= p
->rmargin
- ssz
+ 1;
470 for (i
= 0; i
< ssz
; i
++)
482 header(struct termp
*p
, const struct mdoc_meta
*meta
)
485 const char *pp
, *msec
;
486 size_t ssz
, tsz
, ttsz
, i
;;
488 if (NULL
== (buf
= malloc(p
->rmargin
)))
490 if (NULL
== (title
= malloc(p
->rmargin
)))
493 if (NULL
== (pp
= mdoc_vol2a(meta
->vol
)))
494 switch (meta
->msec
) {
500 pp
= mdoc_vol2a(VOL_URM
);
503 pp
= mdoc_vol2a(VOL_SMM
);
512 pp
= mdoc_vol2a(VOL_PRM
);
515 pp
= mdoc_vol2a(VOL_KM
);
518 /* FIXME: capitalise. */
519 if (NULL
== (pp
= mdoc_msec2a(meta
->msec
)))
520 pp
= mdoc_msec2a(MSEC_local
);
525 tsz
= strlcpy(buf
, pp
, p
->rmargin
);
526 assert(tsz
< p
->rmargin
);
528 if ((pp
= mdoc_arch2a(meta
->arch
))) {
529 tsz
= strlcat(buf
, " (", p
->rmargin
);
530 assert(tsz
< p
->rmargin
);
531 tsz
= strlcat(buf
, pp
, p
->rmargin
);
532 assert(tsz
< p
->rmargin
);
533 tsz
= strlcat(buf
, ")", p
->rmargin
);
534 assert(tsz
< p
->rmargin
);
537 ttsz
= strlcpy(title
, meta
->title
, p
->rmargin
);
539 if (NULL
== (msec
= mdoc_msec2a(meta
->msec
)))
542 ssz
= (2 * (ttsz
+ 2 + strlen(msec
))) + tsz
+ 2;
544 if (ssz
> p
->rmargin
) {
545 if ((ssz
-= p
->rmargin
) % 2)
550 title
[ttsz
- ssz
] = 0;
553 ssz
= ((p
->rmargin
- ssz
) / 2) + 1;
555 printf("%s(%s)", title
, msec
);
557 for (i
= 0; i
< ssz
; i
++)
562 for (i
= 0; i
< ssz
; i
++)
565 printf("%s(%s)\n", title
, msec
);