]>
git.cameronkatri.com Git - mandoc.git/blob - mdocterm.c
1 /* $Id: mdocterm.c,v 1.13 2009/02/25 17:02:47 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
*,
48 const struct mdoc_meta
*,
49 const struct mdoc_node
*);
50 static void header(struct termp
*,
51 const struct mdoc_meta
*);
52 static void footer(struct termp
*,
53 const struct mdoc_meta
*);
55 static void pword(struct termp
*, const char *, size_t);
56 static void pescape(struct termp
*,
57 const char *, size_t *, size_t);
58 static void nescape(struct termp
*,
59 const char *, size_t);
60 static void chara(struct termp
*, char);
61 static void stringa(struct termp
*, const char *);
62 static void style(struct termp
*, enum termstyle
);
65 extern size_t strlcat(char *, const char *, size_t);
66 extern size_t strlcpy(char *, const char *, size_t);
71 main(int argc
, char *argv
[])
74 const struct mdoc
*mdoc
;
79 if ( ! mmain_getopt(p
, argc
, argv
, NULL
, NULL
, NULL
, NULL
))
82 if (NULL
== (mdoc
= mmain_mdoc(p
)))
85 termp
.maxrmargin
= 80; /* XXX */
86 termp
.rmargin
= termp
.maxrmargin
;
88 termp
.offset
= termp
.col
= 0;
89 termp
.flags
= TERMP_NOSPACE
;
91 if (NULL
== (termp
.buf
= malloc(termp
.maxcols
)))
94 header(&termp
, mdoc_meta(mdoc
));
95 body(&termp
, NULL
, mdoc_meta(mdoc
), mdoc_node(mdoc
));
96 footer(&termp
, mdoc_meta(mdoc
));
106 flushln(struct termp
*p
)
108 size_t i
, j
, vsz
, vis
, maxvis
;
111 * First, establish the maximum columns of "visible" content.
112 * This is usually the difference between the right-margin and
113 * an indentation, but can be, for tagged lists or columns, a
114 * small set of values.
117 assert(p
->offset
< p
->rmargin
);
118 maxvis
= p
->rmargin
- p
->offset
;
122 * If in the standard case (left-justified), then begin with our
123 * indentation, otherwise (columns, etc.) just start spitting
127 if ( ! (p
->flags
& TERMP_NOLPAD
))
129 for (j
= 0; j
< p
->offset
; j
++)
133 * If we're literal, print out verbatim.
135 if (p
->flags
& TERMP_LITERAL
) {
136 /* FIXME: count non-printing chars. */
137 for (i
= 0; i
< p
->col
; i
++)
144 for (i
= 0; i
< p
->col
; i
++) {
146 * Count up visible word characters. Control sequences
147 * (starting with the CSI) aren't counted.
149 assert( ! xisspace(p
->buf
[i
]));
152 for (j
= i
, vsz
= 0; j
< p
->col
; j
++) {
153 if (xisspace(p
->buf
[j
]))
155 else if (27 == p
->buf
[j
]) {
156 assert(j
+ 4 <= p
->col
);
164 * If a word is too long and we're within a line, put it
165 * on the next line. Puke if we're being asked to write
166 * something that will exceed the right margin (i.e.,
167 * from a fresh line or when we're not allowed to break
168 * the line with TERMP_NOBREAK).
171 if (vis
&& vis
+ vsz
>= maxvis
) {
173 if (p
->flags
& TERMP_NOBREAK
)
174 errx(1, "word breaks right margin");
176 for (j
= 0; j
< p
->offset
; j
++)
179 } else if (vis
+ vsz
>= maxvis
)
181 errx(1, "word breaks right margin");
184 * Write out the word and a trailing space. Omit the
185 * space if we're the last word in the line.
188 for ( ; i
< p
->col
; i
++) {
189 if (xisspace(p
->buf
[i
]))
201 * If we're not to right-marginalise it (newline), then instead
202 * pad to the right margin and stay off.
205 if (p
->flags
& TERMP_NOBREAK
) {
206 for ( ; vis
< maxvis
; vis
++)
216 newln(struct termp
*p
)
220 * A newline only breaks an existing line; it won't assert
223 p
->flags
|= TERMP_NOSPACE
;
225 p
->flags
&= ~TERMP_NOLPAD
;
229 p
->flags
&= ~TERMP_NOLPAD
;
234 vspace(struct termp
*p
)
238 * Asserts a vertical space (a full, empty line-break between
247 stringa(struct termp
*p
, const char *s
)
250 /* XXX - speed up if not passing to chara. */
257 chara(struct termp
*p
, char c
)
260 /* TODO: dynamically expand the buffer. */
261 if (p
->col
+ 1 >= p
->maxcols
)
262 errx(1, "line overrun");
263 p
->buf
[(p
->col
)++] = c
;
268 style(struct termp
*p
, enum termstyle esc
)
271 if (p
->col
+ 4 >= p
->maxcols
)
272 errx(1, "line overrun");
274 p
->buf
[(p
->col
)++] = 27;
275 p
->buf
[(p
->col
)++] = '[';
278 p
->buf
[(p
->col
)++] = '0';
281 p
->buf
[(p
->col
)++] = '1';
283 case (STYLE_UNDERLINE
):
284 p
->buf
[(p
->col
)++] = '4';
290 p
->buf
[(p
->col
)++] = 'm';
295 nescape(struct termp
*p
, const char *word
, size_t len
)
300 if ('r' == word
[0] && 'B' == word
[1])
302 else if ('l' == word
[0] && 'B' == word
[1])
304 else if ('<' == word
[0] && '-' == word
[1])
306 else if ('-' == word
[0] && '>' == word
[1])
308 else if ('l' == word
[0] && 'q' == word
[1])
310 else if ('r' == word
[0] && 'q' == word
[1])
312 else if ('b' == word
[0] && 'u' == word
[1])
322 pescape(struct termp
*p
, const char *word
, size_t *i
, size_t len
)
329 if ('(' == word
[*i
]) {
330 /* Two-character escapes. */
332 assert(*i
+ 1 < len
);
333 nescape(p
, &word
[*i
], 2);
337 } else if ('[' != word
[*i
]) {
338 /* One-character escapes. */
359 for (j
= 0; word
[*i
] && ']' != word
[*i
]; (*i
)++, j
++)
362 nescape(p
, &word
[*i
- j
], j
);
367 pword(struct termp
*p
, const char *word
, size_t len
)
371 /*assert(len > 0);*/ /* Can be, if literal. */
373 if ( ! (p
->flags
& TERMP_NOSPACE
) &&
374 ! (p
->flags
& TERMP_LITERAL
))
377 if ( ! (p
->flags
& TERMP_NONOSPACE
))
378 p
->flags
&= ~TERMP_NOSPACE
;
380 if (p
->flags
& TERMP_BOLD
)
381 style(p
, STYLE_BOLD
);
382 if (p
->flags
& TERMP_UNDERLINE
)
383 style(p
, STYLE_UNDERLINE
);
385 for (i
= 0; i
< len
; i
++) {
386 if ('\\' == word
[i
]) {
387 pescape(p
, word
, &i
, len
);
393 if (p
->flags
& TERMP_BOLD
||
394 p
->flags
& TERMP_UNDERLINE
)
395 style(p
, STYLE_CLEAR
);
400 word(struct termp
*p
, const char *word
)
404 if (p
->flags
& TERMP_LITERAL
) {
405 pword(p
, word
, strlen(word
));
412 if (mdoc_isdelim(word
)) {
413 if ( ! (p
->flags
& TERMP_IGNDELIM
))
414 p
->flags
|= TERMP_NOSPACE
;
415 p
->flags
&= ~TERMP_IGNDELIM
;
419 for (j
= i
= 0; i
< len
; i
++) {
420 if ( ! xisspace(word
[i
])) {
427 pword(p
, &word
[i
- j
], j
);
432 pword(p
, &word
[i
- j
], j
);
438 body(struct termp
*p
, struct termpair
*ppair
,
439 const struct mdoc_meta
*meta
,
440 const struct mdoc_node
*node
)
443 struct termpair pair
;
445 /* Pre-processing. */
450 pair
.offset
= pair
.rmargin
= 0;
454 if (MDOC_TEXT
!= node
->type
) {
455 if (termacts
[node
->tok
].pre
)
456 if ( ! (*termacts
[node
->tok
].pre
)(p
, &pair
, meta
, node
))
458 } else /* MDOC_TEXT == node->type */
459 word(p
, node
->data
.text
.string
);
463 if (TERMPAIR_FLAG
& pair
.type
)
464 p
->flags
|= pair
.flag
;
466 if (dochild
&& node
->child
)
467 body(p
, &pair
, meta
, node
->child
);
469 if (TERMPAIR_FLAG
& pair
.type
)
470 p
->flags
&= ~pair
.flag
;
472 /* Post-processing. */
474 if (MDOC_TEXT
!= node
->type
)
475 if (termacts
[node
->tok
].post
)
476 (*termacts
[node
->tok
].post
)(p
, &pair
, meta
, node
);
481 body(p
, ppair
, meta
, node
->next
);
486 footer(struct termp
*p
, const struct mdoc_meta
*meta
)
490 size_t sz
, osz
, ssz
, i
;
492 if (NULL
== (buf
= malloc(p
->rmargin
)))
494 if (NULL
== (os
= malloc(p
->rmargin
)))
497 tm
= localtime(&meta
->date
);
500 if (NULL
== strftime(buf
, p
->rmargin
, "%B %d, %Y", tm
))
502 if (0 == strftime(buf
, p
->rmargin
, "%B %d, %Y", tm
))
506 osz
= strlcpy(os
, meta
->os
, p
->rmargin
);
511 if (ssz
> p
->rmargin
) {
517 ssz
= p
->rmargin
- ssz
+ 1;
521 for (i
= 0; i
< ssz
; i
++)
533 header(struct termp
*p
, const struct mdoc_meta
*meta
)
538 if (NULL
== (buf
= malloc(p
->rmargin
)))
540 if (NULL
== (title
= malloc(p
->rmargin
)))
543 if (NULL
== (pp
= mdoc_vol2a(meta
->vol
)))
544 switch (meta
->msec
) {
550 pp
= mdoc_vol2a(VOL_URM
);
553 pp
= mdoc_vol2a(VOL_SMM
);
562 pp
= mdoc_vol2a(VOL_PRM
);
565 pp
= mdoc_vol2a(VOL_KM
);
568 /* FIXME: capitalise. */
569 if (NULL
== (pp
= mdoc_msec2a(meta
->msec
)))
570 pp
= mdoc_msec2a(MSEC_local
);
574 if (mdoc_arch2a(meta
->arch
))
575 (void)snprintf(buf
, p
->rmargin
, "%s(%s)",
576 pp
, mdoc_arch2a(meta
->arch
));
578 (void)strlcpy(buf
, pp
, p
->rmargin
);
580 pp
= mdoc_msec2a(meta
->msec
);
582 (void)snprintf(title
, p
->rmargin
, "%s(%s)",
583 meta
->title
, pp
? pp
: "");
586 p
->rmargin
= (p
->maxrmargin
- strlen(buf
)) / 2;
587 p
->flags
|= TERMP_NOBREAK
;
588 p
->flags
|= TERMP_NOSPACE
;
593 p
->offset
= p
->rmargin
;
594 p
->rmargin
+= strlen(buf
);
601 p
->offset
= p
->rmargin
;
602 p
->rmargin
= p
->maxrmargin
;
603 p
->flags
&= ~TERMP_NOBREAK
;
608 p
->rmargin
= p
->maxrmargin
;
610 p
->flags
&= ~TERMP_NOSPACE
;