]>
git.cameronkatri.com Git - mandoc.git/blob - terminal.c
1 /* $Id: terminal.c,v 1.7 2009/03/23 15:20:51 kristaps Exp $ */
3 * Copyright (c) 2008, 2009 Kristaps Dzonsons <kristaps@openbsd.org>
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.
28 extern size_t strlcpy(char *, const char *, size_t);
29 extern size_t strlcat(char *, const char *, size_t);
32 static struct termp
*term_alloc(enum termenc
);
33 static void term_free(struct termp
*);
34 static void term_body(struct termp
*, struct termpair
*,
35 const struct mdoc_meta
*,
36 const struct mdoc_node
*);
37 static void term_head(struct termp
*,
38 const struct mdoc_meta
*);
39 static void term_foot(struct termp
*,
40 const struct mdoc_meta
*);
41 static void term_pword(struct termp
*, const char *, int);
42 static void term_pescape(struct termp
*,
43 const char *, int *, int);
44 static void term_nescape(struct termp
*,
45 const char *, size_t);
46 static void term_chara(struct termp
*, char);
47 static void term_stringa(struct termp
*,
48 const char *, size_t);
49 static int term_isopendelim(const char *, int);
50 static int term_isclosedelim(const char *, int);
51 static void sanity(const struct mdoc_node
*); /* XXX */
58 return(term_alloc(TERMENC_LATIN1
));
66 return(term_alloc(TERMENC_UTF8
));
74 return(term_alloc(TERMENC_ASCII
));
79 terminal_run(void *arg
, const struct man
*man
,
80 const struct mdoc
*mdoc
)
87 p
= (struct termp
*)arg
;
89 if (NULL
== p
->symtab
)
90 p
->symtab
= term_ascii2htab();
92 term_head(p
, mdoc_meta(mdoc
));
93 term_body(p
, NULL
, mdoc_meta(mdoc
), mdoc_node(mdoc
));
94 term_foot(p
, mdoc_meta(mdoc
));
101 terminal_free(void *arg
)
104 term_free((struct termp
*)arg
);
109 term_free(struct termp
*p
)
114 if (TERMENC_ASCII
== p
->enc
&& p
->symtab
)
115 term_asciifree(p
->symtab
);
121 static struct termp
*
122 term_alloc(enum termenc enc
)
126 if (NULL
== (p
= malloc(sizeof(struct termp
))))
128 bzero(p
, sizeof(struct termp
));
136 term_isclosedelim(const char *p
, int len
)
170 term_isopendelim(const char *p
, int len
)
192 * Flush a line of text. A "line" is loosely defined as being something
193 * that should be followed by a newline, regardless of whether it's
194 * broken apart by newlines getting there. A line can also be a
195 * fragment of a columnar list.
197 * Specifically, a line is whatever's in p->buf of length p->col, which
198 * is zeroed after this function returns.
200 * The variables TERMP_NOLPAD, TERMP_LITERAL and TERMP_NOBREAK are of
201 * critical importance here. Their behaviour follows:
203 * - TERMP_NOLPAD: when beginning to write the line, don't left-pad the
204 * offset value. This is useful when doing columnar lists where the
205 * prior column has right-padded.
207 * - TERMP_NOBREAK: this is the most important and is used when making
208 * columns. In short: don't print a newline and instead pad to the
209 * right margin. Used in conjunction with TERMP_NOLPAD.
211 * - TERMP_NONOBREAK: don't newline when TERMP_NOBREAK is specified.
213 * In-line line breaking:
215 * If TERMP_NOBREAK is specified and the line overruns the right
216 * margin, it will break and pad-right to the right margin after
217 * writing. If maxrmargin is violated, it will break and continue
218 * writing from the right-margin, which will lead to the above
219 * scenario upon exit.
221 * Otherwise, the line will break at the right margin. Extremely long
222 * lines will cause the system to emit a warning (TODO: hyphenate, if
226 term_flushln(struct termp
*p
)
229 size_t vsz
, vis
, maxvis
, mmax
, bp
;
232 * First, establish the maximum columns of "visible" content.
233 * This is usually the difference between the right-margin and
234 * an indentation, but can be, for tagged lists or columns, a
235 * small set of values.
238 assert(p
->offset
< p
->rmargin
);
239 maxvis
= p
->rmargin
- p
->offset
;
240 mmax
= p
->maxrmargin
- p
->offset
;
241 bp
= TERMP_NOBREAK
& p
->flags
? mmax
: maxvis
;
245 * If in the standard case (left-justified), then begin with our
246 * indentation, otherwise (columns, etc.) just start spitting
250 if ( ! (p
->flags
& TERMP_NOLPAD
))
252 for (j
= 0; j
< (int)p
->offset
; j
++)
255 for (i
= 0; i
< (int)p
->col
; i
++) {
257 * Count up visible word characters. Control sequences
258 * (starting with the CSI) aren't counted. A space
259 * generates a non-printing word, which is valid (the
260 * space is printed according to regular spacing rules).
264 for (j
= i
, vsz
= 0; j
< (int)p
->col
; j
++) {
265 if (' ' == p
->buf
[j
])
267 else if (8 == p
->buf
[j
])
274 * Do line-breaking. If we're greater than our
275 * break-point and already in-line, break to the next
276 * line and start writing. If we're at the line start,
277 * then write out the word (TODO: hyphenate) and break
278 * in a subsequent loop invocation.
281 if ( ! (TERMP_NOBREAK
& p
->flags
)) {
282 if (vis
&& vis
+ vsz
> bp
) {
284 for (j
= 0; j
< (int)p
->offset
; j
++)
288 } else if (vis
&& vis
+ vsz
> bp
) {
290 for (j
= 0; j
< (int)p
->rmargin
; j
++)
292 vis
= p
->rmargin
- p
->offset
;
296 * Write out the word and a trailing space. Omit the
297 * space if we're the last word in the line or beyond
301 for ( ; i
< (int)p
->col
; i
++) {
302 if (' ' == p
->buf
[i
])
307 if (i
< (int)p
->col
&& vis
<= bp
) {
314 * If we've overstepped our maximum visible no-break space, then
315 * cause a newline and offset at the right margin.
318 if ((TERMP_NOBREAK
& p
->flags
) && vis
>= maxvis
) {
319 if ( ! (TERMP_NONOBREAK
& p
->flags
)) {
321 for (i
= 0; i
< (int)p
->rmargin
; i
++)
329 * If we're not to right-marginalise it (newline), then instead
330 * pad to the right margin and stay off.
333 if (p
->flags
& TERMP_NOBREAK
) {
334 if ( ! (TERMP_NONOBREAK
& p
->flags
))
335 for ( ; vis
< maxvis
; vis
++)
345 * A newline only breaks an existing line; it won't assert vertical
346 * space. All data in the output buffer is flushed prior to the newline
350 term_newln(struct termp
*p
)
353 p
->flags
|= TERMP_NOSPACE
;
355 p
->flags
&= ~TERMP_NOLPAD
;
359 p
->flags
&= ~TERMP_NOLPAD
;
364 * Asserts a vertical space (a full, empty line-break between lines).
365 * Note that if used twice, this will cause two blank spaces and so on.
366 * All data in the output buffer is flushed prior to the newline
370 term_vspace(struct termp
*p
)
379 * Break apart a word into "pwords" (partial-words, usually from
380 * breaking up a phrase into individual words) and, eventually, put them
381 * into the output buffer. If we're a literal word, then don't break up
382 * the word and put it verbatim into the output buffer.
385 term_word(struct termp
*p
, const char *word
)
389 len
= (int)strlen(word
);
391 if (p
->flags
& TERMP_LITERAL
) {
392 term_pword(p
, word
, len
);
397 for (j
= i
= 0; i
< len
; i
++) {
398 if (' ' != word
[i
]) {
403 /* Escaped spaces don't delimit... */
404 if (i
&& ' ' == word
[i
] && '\\' == word
[i
- 1]) {
412 term_pword(p
, &word
[i
- j
], j
);
417 term_pword(p
, &word
[i
- j
], j
);
423 term_body(struct termp
*p
, struct termpair
*ppair
,
424 const struct mdoc_meta
*meta
,
425 const struct mdoc_node
*node
)
428 term_node(p
, ppair
, meta
, node
);
430 term_body(p
, ppair
, meta
, node
->next
);
435 * This is the main function for printing out nodes. It's constituted
436 * of PRE and POST functions, which correspond to prefix and infix
437 * processing. The termpair structure allows data to persist between
438 * prefix and postfix invocations.
441 term_node(struct termp
*p
, struct termpair
*ppair
,
442 const struct mdoc_meta
*meta
,
443 const struct mdoc_node
*node
)
446 struct termpair pair
;
448 /* Some quick sanity-checking. */
452 /* Pre-processing. */
457 pair
.offset
= pair
.rmargin
= 0;
461 if (MDOC_TEXT
!= node
->type
) {
462 if (termacts
[node
->tok
].pre
)
463 if ( ! (*termacts
[node
->tok
].pre
)(p
, &pair
, meta
, node
))
465 } else /* MDOC_TEXT == node->type */
466 term_word(p
, node
->string
);
470 if (TERMPAIR_FLAG
& pair
.type
)
471 p
->flags
|= pair
.flag
;
473 if (dochild
&& node
->child
)
474 term_body(p
, &pair
, meta
, node
->child
);
476 if (TERMPAIR_FLAG
& pair
.type
)
477 p
->flags
&= ~pair
.flag
;
479 /* Post-processing. */
481 if (MDOC_TEXT
!= node
->type
)
482 if (termacts
[node
->tok
].post
)
483 (*termacts
[node
->tok
].post
)(p
, &pair
, meta
, node
);
488 term_foot(struct termp
*p
, const struct mdoc_meta
*meta
)
493 if (NULL
== (buf
= malloc(p
->rmargin
)))
495 if (NULL
== (os
= malloc(p
->rmargin
)))
498 tm
= localtime(&meta
->date
);
501 if (NULL
== strftime(buf
, p
->rmargin
, "%B %d, %Y", tm
))
503 if (0 == strftime(buf
, p
->rmargin
, "%B %d, %Y", tm
))
507 (void)strlcpy(os
, meta
->os
, p
->rmargin
);
510 * This is /slightly/ different from regular groff output
511 * because we don't have page numbers. Print the following:
518 p
->flags
|= TERMP_NOSPACE
| TERMP_NOBREAK
;
519 p
->rmargin
= p
->maxrmargin
- strlen(buf
);
525 p
->flags
|= TERMP_NOLPAD
| TERMP_NOSPACE
;
526 p
->offset
= p
->rmargin
;
527 p
->rmargin
= p
->maxrmargin
;
528 p
->flags
&= ~TERMP_NOBREAK
;
539 term_head(struct termp
*p
, const struct mdoc_meta
*meta
)
543 p
->rmargin
= p
->maxrmargin
;
546 if (NULL
== (buf
= malloc(p
->rmargin
)))
548 if (NULL
== (title
= malloc(p
->rmargin
)))
552 * The header is strange. It has three components, which are
553 * really two with the first duplicated. It goes like this:
555 * IDENTIFIER TITLE IDENTIFIER
557 * The IDENTIFIER is NAME(SECTION), which is the command-name
558 * (if given, or "unknown" if not) followed by the manual page
559 * section. These are given in `Dt'. The TITLE is a free-form
560 * string depending on the manual volume. If not specified, it
561 * switches on the manual section.
565 (void)strlcpy(buf
, meta
->vol
, p
->rmargin
);
568 (void)strlcat(buf
, " (", p
->rmargin
);
569 (void)strlcat(buf
, meta
->arch
, p
->rmargin
);
570 (void)strlcat(buf
, ")", p
->rmargin
);
573 (void)snprintf(title
, p
->rmargin
, "%s(%d)",
574 meta
->title
, meta
->msec
);
577 p
->rmargin
= (p
->maxrmargin
- strlen(buf
)) / 2;
578 p
->flags
|= TERMP_NOBREAK
| TERMP_NOSPACE
;
583 p
->flags
|= TERMP_NOLPAD
| TERMP_NOSPACE
;
584 p
->offset
= p
->rmargin
;
585 p
->rmargin
= p
->maxrmargin
- strlen(title
);
590 p
->offset
= p
->rmargin
;
591 p
->rmargin
= p
->maxrmargin
;
592 p
->flags
&= ~TERMP_NOBREAK
;
593 p
->flags
|= TERMP_NOLPAD
| TERMP_NOSPACE
;
598 p
->rmargin
= p
->maxrmargin
;
600 p
->flags
&= ~TERMP_NOSPACE
;
608 * Determine the symbol indicated by an escape sequences, that is, one
609 * starting with a backslash. Once done, we pass this value into the
610 * output buffer by way of the symbol table.
613 term_nescape(struct termp
*p
, const char *word
, size_t len
)
618 if (NULL
== (rhs
= term_a2ascii(p
->symtab
, word
, len
, &sz
)))
620 term_stringa(p
, rhs
, sz
);
625 * Handle an escape sequence: determine its length and pass it to the
626 * escape-symbol look table. Note that we assume mdoc(3) has validated
627 * the escape sequence (we assert upon badly-formed escape sequences).
630 term_pescape(struct termp
*p
, const char *word
, int *i
, int len
)
637 if ('(' == word
[*i
]) {
642 term_nescape(p
, &word
[*i
], 2);
646 } else if ('*' == word
[*i
]) {
657 term_nescape(p
, &word
[*i
], 2);
663 term_nescape(p
, &word
[*i
], 1);
667 } else if ('[' != word
[*i
]) {
668 term_nescape(p
, &word
[*i
], 1);
673 for (j
= 0; word
[*i
] && ']' != word
[*i
]; (*i
)++, j
++)
679 term_nescape(p
, &word
[*i
- j
], (size_t)j
);
684 * Handle pwords, partial words, which may be either a single word or a
685 * phrase that cannot be broken down (such as a literal string). This
686 * handles word styling.
689 term_pword(struct termp
*p
, const char *word
, int len
)
693 if (term_isclosedelim(word
, len
))
694 if ( ! (TERMP_IGNDELIM
& p
->flags
))
695 p
->flags
|= TERMP_NOSPACE
;
697 if ( ! (TERMP_NOSPACE
& p
->flags
))
700 if ( ! (p
->flags
& TERMP_NONOSPACE
))
701 p
->flags
&= ~TERMP_NOSPACE
;
704 * If ANSI (word-length styling), then apply our style now,
708 for (i
= 0; i
< len
; i
++) {
709 if ('\\' == word
[i
]) {
710 term_pescape(p
, word
, &i
, len
);
714 if (TERMP_STYLE
& p
->flags
) {
715 if (TERMP_BOLD
& p
->flags
) {
716 term_chara(p
, word
[i
]);
719 if (TERMP_UNDER
& p
->flags
) {
725 term_chara(p
, word
[i
]);
728 if (term_isopendelim(word
, len
))
729 p
->flags
|= TERMP_NOSPACE
;
734 * Like term_chara() but for arbitrary-length buffers. Resize the
735 * buffer by a factor of two (if the buffer is less than that) or the
739 term_stringa(struct termp
*p
, const char *c
, size_t sz
)
747 if (p
->col
+ sz
>= p
->maxcols
) {
750 s
= sz
> p
->maxcols
* 2 ? sz
: p
->maxcols
* 2;
751 p
->buf
= realloc(p
->buf
, s
);
757 (void)memcpy(&p
->buf
[(int)p
->col
], c
, sz
);
763 * Insert a single character into the line-buffer. If the buffer's
764 * space is exceeded, then allocate more space by doubling the buffer
768 term_chara(struct termp
*p
, char c
)
772 if (p
->col
+ 1 >= p
->maxcols
) {
776 p
->buf
= realloc(p
->buf
, s
);
781 p
->buf
[(int)(p
->col
)++] = c
;
786 sanity(const struct mdoc_node
*n
)
792 errx(1, "regular form violated (1)");
793 if (NULL
== n
->parent
)
794 errx(1, "regular form violated (2)");
795 if (NULL
== n
->string
)
796 errx(1, "regular form violated (3)");
797 switch (n
->parent
->type
) {
801 errx(1, "regular form violated (4)");
808 if (NULL
== n
->parent
)
809 errx(1, "regular form violated (5)");
810 switch (n
->parent
->type
) {
818 errx(1, "regular form violated (6)");
821 if (n
->child
) switch (n
->child
->type
) {
825 errx(1, "regular form violated (7(");
834 if (NULL
== n
->parent
)
835 errx(1, "regular form violated (8)");
836 if (MDOC_BLOCK
!= n
->parent
->type
)
837 errx(1, "regular form violated (9)");
838 if (n
->child
) switch (n
->child
->type
) {
846 errx(1, "regular form violated (a)");
851 if (NULL
== n
->parent
)
852 errx(1, "regular form violated (b)");
853 if (NULL
== n
->child
)
854 errx(1, "regular form violated (c)");
855 switch (n
->parent
->type
) {
865 errx(1, "regular form violated (d)");
868 switch (n
->child
->type
) {
872 errx(1, "regular form violated (e)");
880 errx(1, "regular form violated (f)");
881 if (NULL
== n
->child
)
882 errx(1, "regular form violated (10)");
883 switch (n
->child
->type
) {
887 errx(1, "regular form violated (11)");