]>
git.cameronkatri.com Git - mandoc.git/blob - mdocterm.c
1 /* $Id: mdocterm.c,v 1.43 2009/03/15 07:08:53 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.
19 #include <sys/types.h>
41 struct termp
*termp
; /* Ephemeral. */
49 dead_pre
void punt(struct nroffopt
*, char *) dead_post
;
50 static int option(void *, int, char *);
51 static int optsopt(struct termp
*, char *);
52 static void body(struct termp
*,
54 const struct mdoc_meta
*,
55 const struct mdoc_node
*);
56 static void header(struct termp
*,
57 const struct mdoc_meta
*);
58 static void footer(struct termp
*,
59 const struct mdoc_meta
*);
61 static void pword(struct termp
*, const char *, size_t);
62 static void pescape(struct termp
*, const char *,
64 static void nescape(struct termp
*,
65 const char *, size_t);
66 static void chara(struct termp
*, char);
67 static void stringa(struct termp
*,
68 const char *, size_t);
69 static void symbola(struct termp
*, enum tsym
);
70 static void sanity(const struct mdoc_node
*);
73 extern size_t strlcat(char *, const char *, size_t);
74 extern size_t strlcpy(char *, const char *, size_t);
77 static struct termseq termenc1
[] = {
78 { "\\", TERMSYM_SLASH
},
79 { "\'", TERMSYM_RSQUOTE
},
80 { "`", TERMSYM_LSQUOTE
},
81 { "-", TERMSYM_HYPHEN
},
82 { " ", TERMSYM_SPACE
},
83 { ".", TERMSYM_PERIOD
},
84 { "&", TERMSYM_BREAK
},
85 { "e", TERMSYM_SLASH
},
86 { "q", TERMSYM_DQUOTE
},
87 { "|", TERMSYM_BREAK
},
91 static struct termseq termenc2
[] = {
92 { "rC", TERMSYM_RBRACE
},
93 { "lC", TERMSYM_LBRACE
},
94 { "rB", TERMSYM_RBRACK
},
95 { "lB", TERMSYM_LBRACK
},
96 { "ra", TERMSYM_RANGLE
},
97 { "la", TERMSYM_LANGLE
},
98 { "Lq", TERMSYM_LDQUOTE
},
99 { "lq", TERMSYM_LDQUOTE
},
100 { "Rq", TERMSYM_RDQUOTE
},
101 { "rq", TERMSYM_RDQUOTE
},
102 { "oq", TERMSYM_LSQUOTE
},
103 { "aq", TERMSYM_RSQUOTE
},
105 { "<-", TERMSYM_LARROW
},
106 { "->", TERMSYM_RARROW
},
107 { "ua", TERMSYM_UARROW
},
108 { "da", TERMSYM_DARROW
},
110 { "bu", TERMSYM_BULLET
},
111 { "Ba", TERMSYM_BAR
},
112 { "ba", TERMSYM_BAR
},
113 { "co", TERMSYM_COPY
},
114 { "Am", TERMSYM_AMP
},
116 { "Le", TERMSYM_LE
},
117 { "<=", TERMSYM_LE
},
118 { "Ge", TERMSYM_GE
},
119 { ">=", TERMSYM_GE
},
120 { "==", TERMSYM_EQ
},
121 { "Ne", TERMSYM_NEQ
},
122 { "!=", TERMSYM_NEQ
},
123 { "Pm", TERMSYM_PLUSMINUS
},
124 { "+-", TERMSYM_PLUSMINUS
},
125 { "If", TERMSYM_INF2
},
126 { "if", TERMSYM_INF
},
127 { "Na", TERMSYM_NAN
},
128 { "na", TERMSYM_NAN
},
129 { "**", TERMSYM_ASTERISK
},
130 { "Gt", TERMSYM_GT
},
131 { "Lt", TERMSYM_LT
},
133 { "aa", TERMSYM_ACUTE
},
134 { "ga", TERMSYM_GRAVE
},
136 { "en", TERMSYM_EN
},
137 { "em", TERMSYM_EM
},
139 { "Pi", TERMSYM_PI
},
143 /* FIXME: abstract to dynamically-compiled table. */
144 static struct termsym termsym_ascii
[TERMSYM_MAX
] = {
145 { "]", 1 }, /* TERMSYM_RBRACK */
146 { "[", 1 }, /* TERMSYM_LBRACK */
147 { "<-", 2 }, /* TERMSYM_LARROW */
148 { "->", 2 }, /* TERMSYM_RARROW */
149 { "^", 1 }, /* TERMSYM_UARROW */
150 { "v", 1 }, /* TERMSYM_DARROW */
151 { "`", 1 }, /* TERMSYM_LSQUOTE */
152 { "\'", 1 }, /* TERMSYM_RSQUOTE */
153 { "\'", 1 }, /* TERMSYM_SQUOTE */
154 { "``", 2 }, /* TERMSYM_LDQUOTE */
155 { "\'\'", 2 }, /* TERMSYM_RDQUOTE */
156 { "\"", 1 }, /* TERMSYM_DQUOTE */
157 { "<", 1 }, /* TERMSYM_LT */
158 { ">", 1 }, /* TERMSYM_GT */
159 { "<=", 2 }, /* TERMSYM_LE */
160 { ">=", 2 }, /* TERMSYM_GE */
161 { "==", 2 }, /* TERMSYM_EQ */
162 { "!=", 2 }, /* TERMSYM_NEQ */
163 { "\'", 1 }, /* TERMSYM_ACUTE */
164 { "`", 1 }, /* TERMSYM_GRAVE */
165 { "pi", 2 }, /* TERMSYM_PI */
166 { "+=", 2 }, /* TERMSYM_PLUSMINUS */
167 { "oo", 2 }, /* TERMSYM_INF */
168 { "infinity", 8 }, /* TERMSYM_INF2 */
169 { "NaN", 3 }, /* TERMSYM_NAN */
170 { "|", 1 }, /* TERMSYM_BAR */
171 { "o", 1 }, /* TERMSYM_BULLET */
172 { "&", 1 }, /* TERMSYM_AMP */
173 { "--", 2 }, /* TERMSYM_EM */
174 { "-", 1 }, /* TERMSYM_EN */
175 { "(C)", 3 }, /* TERMSYM_COPY */
176 { "*", 1 }, /* TERMSYM_ASTERISK */
177 { "\\", 1 }, /* TERMSYM_SLASH */
178 { "-", 1 }, /* TERMSYM_HYPHEN */
179 { " ", 1 }, /* TERMSYM_SPACE */
180 { ".", 1 }, /* TERMSYM_PERIOD */
181 { "", 0 }, /* TERMSYM_BREAK */
182 { "<", 1 }, /* TERMSYM_LANGLE */
183 { ">", 1 }, /* TERMSYM_RANGLE */
184 { "{", 1 }, /* TERMSYM_LBRACE */
185 { "}", 1 }, /* TERMSYM_RBRACE */
189 main(int argc
, char *argv
[])
192 const struct mdoc
*mdoc
;
193 struct nroffopt nroff
;
198 (void)memset(&termp
, 0, sizeof(struct termp
));
199 (void)memset(&nroff
, 0, sizeof(struct nroffopt
));
201 termp
.maxrmargin
= termp
.rmargin
= 78; /* FIXME */
202 termp
.maxcols
= 1024; /* FIXME */
203 termp
.offset
= termp
.col
= 0;
204 termp
.flags
= TERMP_NOSPACE
;
205 termp
.symtab
= termsym_ascii
;
206 termp
.enc
= TERMENC_NROFF
;
208 nroff
.termp
= &termp
;
212 c
= mmain_getopt(p
, argc
, argv
, "[-Ooption...]",
213 "[infile]", "him:n:o:r:T:O:", &nroff
, option
);
215 /* FIXME: this needs to accept multiple outputs. */
222 mmain_prepare(p
, in
);
224 if (NULL
== (mdoc
= mmain_process(p
))) {
225 if (TERMP_NOPUNT
& termp
.iflags
)
232 if (NULL
== (termp
.buf
= malloc(termp
.maxcols
)))
235 header(&termp
, mdoc_meta(mdoc
));
236 body(&termp
, NULL
, mdoc_meta(mdoc
), mdoc_node(mdoc
));
237 footer(&termp
, mdoc_meta(mdoc
));
247 optsopt(struct termp
*p
, char *arg
)
250 char *toks
[] = { "ansi", "nopunt", NULL
};
253 switch (getsubopt(&arg
, toks
, &v
)) {
255 p
->enc
= TERMENC_ANSI
;
258 p
->iflags
|= TERMP_NOPUNT
;
261 warnx("unknown -O argument");
270 option(void *ptr
, int c
, char *arg
)
273 struct nroffopt
*nroff
;
275 nroff
= (struct nroffopt
*)ptr
;
276 termp
= nroff
->termp
;
301 return(optsopt(termp
, arg
));
311 * Flush a line of text. A "line" is loosely defined as being something
312 * that should be followed by a newline, regardless of whether it's
313 * broken apart by newlines getting there. A line can also be a
314 * fragment of a columnar list.
316 * Specifically, a line is whatever's in p->buf of length p->col, which
317 * is zeroed after this function returns.
319 * The variables TERMP_NOLPAD, TERMP_LITERAL and TERMP_NOBREAK are of
320 * critical importance here. Their behaviour follows:
322 * - TERMP_NOLPAD: when beginning to write the line, don't left-pad the
323 * offset value. This is useful when doing columnar lists where the
324 * prior column has right-padded.
326 * - TERMP_NOBREAK: this is the most important and is used when making
327 * columns. In short: don't print a newline and instead pad to the
328 * right margin. Used in conjunction with TERMP_NOLPAD.
330 * In-line line breaking:
332 * If TERMP_NOBREAK is specified and the line overruns the right
333 * margin, it will break and pad-right to the right margin after
334 * writing. If maxrmargin is violated, it will break and continue
335 * writing from the right-margin, which will lead to the above
336 * scenario upon exit.
338 * Otherwise, the line will break at the right margin. Extremely long
339 * lines will cause the system to emit a warning (TODO: hyphenate, if
343 flushln(struct termp
*p
)
345 size_t i
, j
, vsz
, vis
, maxvis
, mmax
, bp
;
348 * First, establish the maximum columns of "visible" content.
349 * This is usually the difference between the right-margin and
350 * an indentation, but can be, for tagged lists or columns, a
351 * small set of values.
354 assert(p
->offset
< p
->rmargin
);
355 maxvis
= p
->rmargin
- p
->offset
;
356 mmax
= p
->maxrmargin
- p
->offset
;
357 bp
= TERMP_NOBREAK
& p
->flags
? mmax
: maxvis
;
361 * If in the standard case (left-justified), then begin with our
362 * indentation, otherwise (columns, etc.) just start spitting
366 if ( ! (p
->flags
& TERMP_NOLPAD
))
368 for (j
= 0; j
< p
->offset
; j
++)
371 for (i
= 0; i
< p
->col
; i
++) {
373 * Count up visible word characters. Control sequences
374 * (starting with the CSI) aren't counted. A space
375 * generates a non-printing word, which is valid (the
376 * space is printed according to regular spacing rules).
380 for (j
= i
, vsz
= 0; j
< p
->col
; j
++) {
381 if (isspace((u_char
)p
->buf
[j
])) {
383 } else if (27 == p
->buf
[j
]) {
384 assert(TERMENC_ANSI
== p
->enc
);
385 assert(j
+ 5 <= p
->col
);
387 } else if (8 == p
->buf
[j
]) {
388 assert(TERMENC_NROFF
== p
->enc
);
389 assert(j
+ 2 <= p
->col
);
396 * Do line-breaking. If we're greater than our
397 * break-point and already in-line, break to the next
398 * line and start writing. If we're at the line start,
399 * then write out the word (TODO: hyphenate) and break
400 * in a subsequent loop invocation.
403 if ( ! (TERMP_NOBREAK
& p
->flags
)) {
404 if (vis
&& vis
+ vsz
> bp
) {
406 for (j
= 0; j
< p
->offset
; j
++)
409 } else if (vis
+ vsz
> bp
)
410 warnx("word breaks right margin");
412 /* TODO: hyphenate. */
415 if (vis
&& vis
+ vsz
> bp
) {
417 for (j
= 0; j
< p
->rmargin
; j
++)
419 vis
= p
->rmargin
- p
->offset
;
420 } else if (vis
+ vsz
> bp
)
421 warnx("word breaks right margin");
423 /* TODO: hyphenate. */
427 * Write out the word and a trailing space. Omit the
428 * space if we're the last word in the line or beyond
432 for ( ; i
< p
->col
; i
++) {
433 if (isspace((u_char
)p
->buf
[i
]))
438 if (i
< p
->col
&& vis
<= bp
) {
445 * If we've overstepped our maximum visible no-break space, then
446 * cause a newline and offset at the right margin.
449 if ((TERMP_NOBREAK
& p
->flags
) && vis
>= maxvis
) {
450 if ( ! (TERMP_NONOBREAK
& p
->flags
)) {
452 for (i
= 0; i
< p
->rmargin
; i
++)
460 * If we're not to right-marginalise it (newline), then instead
461 * pad to the right margin and stay off.
464 if (p
->flags
& TERMP_NOBREAK
) {
465 if ( ! (TERMP_NONOBREAK
& p
->flags
))
466 for ( ; vis
< maxvis
; vis
++)
476 * A newline only breaks an existing line; it won't assert vertical
477 * space. All data in the output buffer is flushed prior to the newline
481 newln(struct termp
*p
)
484 p
->flags
|= TERMP_NOSPACE
;
486 p
->flags
&= ~TERMP_NOLPAD
;
490 p
->flags
&= ~TERMP_NOLPAD
;
495 * Asserts a vertical space (a full, empty line-break between lines).
496 * Note that if used twice, this will cause two blank spaces and so on.
497 * All data in the output buffer is flushed prior to the newline
501 vspace(struct termp
*p
)
510 * Break apart a word into "pwords" (partial-words, usually from
511 * breaking up a phrase into individual words) and, eventually, put them
512 * into the output buffer. If we're a literal word, then don't break up
513 * the word and put it verbatim into the output buffer.
516 word(struct termp
*p
, const char *word
)
520 if (p
->flags
& TERMP_LITERAL
) {
521 pword(p
, word
, strlen(word
));
525 if (0 == (len
= strlen(word
)))
526 errx(1, "blank line not in literal context");
528 if (mdoc_isdelim(word
)) {
529 if ( ! (p
->flags
& TERMP_IGNDELIM
))
530 p
->flags
|= TERMP_NOSPACE
;
531 p
->flags
&= ~TERMP_IGNDELIM
;
535 for (j
= i
= 0; i
< len
; i
++) {
536 if ( ! isspace((u_char
)word
[i
])) {
541 /* Escaped spaces don't delimit... */
542 if (i
> 0 && isspace((u_char
)word
[i
]) &&
543 '\\' == word
[i
- 1]) {
551 pword(p
, &word
[i
- j
], j
);
556 pword(p
, &word
[i
- j
], j
);
562 * This is the main function for printing out nodes. It's constituted
563 * of PRE and POST functions, which correspond to prefix and infix
564 * processing. The termpair structure allows data to persist between
565 * prefix and postfix invocations.
568 body(struct termp
*p
, struct termpair
*ppair
,
569 const struct mdoc_meta
*meta
,
570 const struct mdoc_node
*node
)
573 struct termpair pair
;
575 /* Some quick sanity-checking. */
579 /* Pre-processing. */
584 pair
.offset
= pair
.rmargin
= 0;
588 if (MDOC_TEXT
!= node
->type
) {
589 if (termacts
[node
->tok
].pre
)
590 if ( ! (*termacts
[node
->tok
].pre
)(p
, &pair
, meta
, node
))
592 } else /* MDOC_TEXT == node->type */
593 word(p
, node
->string
);
597 if (TERMPAIR_FLAG
& pair
.type
)
598 p
->flags
|= pair
.flag
;
600 if (dochild
&& node
->child
)
601 body(p
, &pair
, meta
, node
->child
);
603 if (TERMPAIR_FLAG
& pair
.type
)
604 p
->flags
&= ~pair
.flag
;
606 /* Post-processing. */
608 if (MDOC_TEXT
!= node
->type
)
609 if (termacts
[node
->tok
].post
)
610 (*termacts
[node
->tok
].post
)(p
, &pair
, meta
, node
);
615 body(p
, ppair
, meta
, node
->next
);
620 footer(struct termp
*p
, const struct mdoc_meta
*meta
)
625 if (NULL
== (buf
= malloc(p
->rmargin
)))
627 if (NULL
== (os
= malloc(p
->rmargin
)))
630 tm
= localtime(&meta
->date
);
633 if (NULL
== strftime(buf
, p
->rmargin
, "%B %d, %Y", tm
))
635 if (0 == strftime(buf
, p
->rmargin
, "%B %d, %Y", tm
))
639 (void)strlcpy(os
, meta
->os
, p
->rmargin
);
642 * This is /slightly/ different from regular groff output
643 * because we don't have page numbers. Print the following:
650 p
->flags
|= TERMP_NOSPACE
| TERMP_NOBREAK
;
651 p
->rmargin
= p
->maxrmargin
- strlen(buf
);
657 p
->flags
|= TERMP_NOLPAD
| TERMP_NOSPACE
;
658 p
->offset
= p
->rmargin
;
659 p
->rmargin
= p
->maxrmargin
;
660 p
->flags
&= ~TERMP_NOBREAK
;
671 header(struct termp
*p
, const struct mdoc_meta
*meta
)
673 char *buf
, *title
, *bufp
;
675 p
->rmargin
= p
->maxrmargin
;
678 if (NULL
== (buf
= malloc(p
->rmargin
)))
680 if (NULL
== (title
= malloc(p
->rmargin
)))
684 * The header is strange. It has three components, which are
685 * really two with the first duplicated. It goes like this:
687 * IDENTIFIER TITLE IDENTIFIER
689 * The IDENTIFIER is NAME(SECTION), which is the command-name
690 * (if given, or "unknown" if not) followed by the manual page
691 * section. These are given in `Dt'. The TITLE is a free-form
692 * string depending on the manual volume. If not specified, it
693 * switches on the manual section.
697 (void)strlcpy(buf
, meta
->vol
, p
->rmargin
);
700 (void)strlcat(buf
, " (", p
->rmargin
);
701 (void)strlcat(buf
, meta
->arch
, p
->rmargin
);
702 (void)strlcat(buf
, ")", p
->rmargin
);
705 (void)snprintf(title
, p
->rmargin
, "%s(%d)",
706 meta
->title
, meta
->msec
);
708 for (bufp
= title
; *bufp
; bufp
++)
709 *bufp
= toupper((u_char
)*bufp
);
712 p
->rmargin
= (p
->maxrmargin
- strlen(buf
)) / 2;
713 p
->flags
|= TERMP_NOBREAK
| TERMP_NOSPACE
;
718 p
->flags
|= TERMP_NOLPAD
| TERMP_NOSPACE
;
719 p
->offset
= p
->rmargin
;
720 p
->rmargin
= p
->maxrmargin
- strlen(title
);
725 p
->offset
= p
->rmargin
;
726 p
->rmargin
= p
->maxrmargin
;
727 p
->flags
&= ~TERMP_NOBREAK
;
728 p
->flags
|= TERMP_NOLPAD
| TERMP_NOSPACE
;
733 p
->rmargin
= p
->maxrmargin
;
735 p
->flags
&= ~TERMP_NOSPACE
;
743 * Determine the symbol indicated by an escape sequences, that is, one
744 * starting with a backslash. Once done, we pass this value into the
745 * output buffer by way of the symbol table.
748 nescape(struct termp
*p
, const char *word
, size_t len
)
760 warnx("unsupported %zu-byte escape sequence", len
);
764 for ( ; enc
->enc
; enc
++)
765 if (0 == memcmp(enc
->enc
, word
, len
)) {
766 symbola(p
, enc
->sym
);
770 warnx("unsupported %zu-byte escape sequence", len
);
775 * Handle an escape sequence: determine its length and pass it to the
776 * escape-symbol look table. Note that we assume mdoc(3) has validated
777 * the escape sequence (we assert upon badly-formed escape sequences).
780 pescape(struct termp
*p
, const char *word
, size_t *i
, size_t len
)
785 warnx("ignoring bad escape sequence");
789 if ('(' == word
[*i
]) {
792 warnx("ignoring bad escape sequence");
795 nescape(p
, &word
[*i
], 2);
799 } else if ('*' == word
[*i
]) {
802 warnx("ignoring bad escape sequence");
809 warnx("ignoring bad escape sequence");
812 nescape(p
, &word
[*i
], 2);
818 nescape(p
, &word
[*i
], 1);
822 } else if ('[' != word
[*i
]) {
823 nescape(p
, &word
[*i
], 1);
828 for (j
= 0; word
[*i
] && ']' != word
[*i
]; (*i
)++, j
++)
832 warnx("ignoring bad escape sequence");
835 nescape(p
, &word
[*i
- j
], j
);
840 * Handle pwords, partial words, which may be either a single word or a
841 * phrase that cannot be broken down (such as a literal string). This
842 * handles word styling.
845 pword(struct termp
*p
, const char *word
, size_t len
)
849 if ( ! (TERMP_NOSPACE
& p
->flags
) &&
850 ! (TERMP_LITERAL
& p
->flags
))
853 if ( ! (p
->flags
& TERMP_NONOSPACE
))
854 p
->flags
&= ~TERMP_NOSPACE
;
857 * If ANSI (word-length styling), then apply our style now,
861 if (TERMENC_ANSI
== p
->enc
&& TERMP_STYLE
& p
->flags
) {
862 if (TERMP_BOLD
& p
->flags
) {
864 stringa(p
, "[01m", 4);
866 if (TERMP_UNDER
& p
->flags
) {
868 stringa(p
, "[04m", 4);
870 if (TERMP_RED
& p
->flags
) {
872 stringa(p
, "[31m", 4);
874 if (TERMP_GREEN
& p
->flags
) {
876 stringa(p
, "[32m", 4);
878 if (TERMP_YELLOW
& p
->flags
) {
880 stringa(p
, "[33m", 4);
882 if (TERMP_BLUE
& p
->flags
) {
884 stringa(p
, "[34m", 4);
886 if (TERMP_MAGENTA
& p
->flags
) {
888 stringa(p
, "[35m", 4);
890 if (TERMP_CYAN
& p
->flags
) {
892 stringa(p
, "[36m", 4);
896 for (i
= 0; i
< len
; i
++) {
897 if ('\\' == word
[i
]) {
898 pescape(p
, word
, &i
, len
);
902 if (TERMENC_NROFF
== p
->enc
&&
903 TERMP_STYLE
& p
->flags
) {
904 if (TERMP_BOLD
& p
->flags
) {
908 if (TERMP_UNDER
& p
->flags
) {
917 if (TERMENC_ANSI
== p
->enc
&& TERMP_STYLE
& p
->flags
) {
919 stringa(p
, "[00m", 4);
925 * Add a symbol to the output line buffer.
928 symbola(struct termp
*p
, enum tsym sym
)
931 assert(p
->symtab
[sym
].sym
);
932 stringa(p
, p
->symtab
[sym
].sym
, p
->symtab
[sym
].sz
);
937 * Like chara() but for arbitrary-length buffers. Resize the buffer by
938 * a factor of two (if the buffer is less than that) or the buffer's
942 stringa(struct termp
*p
, const char *c
, size_t sz
)
949 s
= sz
> p
->maxcols
* 2 ? sz
: p
->maxcols
* 2;
952 if (p
->col
+ sz
>= p
->maxcols
) {
953 p
->buf
= realloc(p
->buf
, s
);
959 (void)memcpy(&p
->buf
[p
->col
], c
, sz
);
965 * Insert a single character into the line-buffer. If the buffer's
966 * space is exceeded, then allocate more space by doubling the buffer
970 chara(struct termp
*p
, char c
)
973 if (p
->col
+ 1 >= p
->maxcols
) {
974 p
->buf
= realloc(p
->buf
, p
->maxcols
* 2);
979 p
->buf
[(p
->col
)++] = c
;
984 sanity(const struct mdoc_node
*n
)
990 errx(1, "regular form violated (1)");
991 if (NULL
== n
->parent
)
992 errx(1, "regular form violated (2)");
993 if (NULL
== n
->string
)
994 errx(1, "regular form violated (3)");
995 switch (n
->parent
->type
) {
999 errx(1, "regular form violated (4)");
1006 if (NULL
== n
->parent
)
1007 errx(1, "regular form violated (5)");
1008 switch (n
->parent
->type
) {
1016 errx(1, "regular form violated (6)");
1019 if (n
->child
) switch (n
->child
->type
) {
1023 errx(1, "regular form violated (7(");
1032 if (NULL
== n
->parent
)
1033 errx(1, "regular form violated (8)");
1034 if (MDOC_BLOCK
!= n
->parent
->type
)
1035 errx(1, "regular form violated (9)");
1036 if (n
->child
) switch (n
->child
->type
) {
1044 errx(1, "regular form violated (a)");
1049 if (NULL
== n
->parent
)
1050 errx(1, "regular form violated (b)");
1051 if (NULL
== n
->child
)
1052 errx(1, "regular form violated (c)");
1053 switch (n
->parent
->type
) {
1063 errx(1, "regular form violated (d)");
1066 switch (n
->child
->type
) {
1070 errx(1, "regular form violated (e)");
1078 errx(1, "regular form violated (f)");
1079 if (NULL
== n
->child
)
1080 errx(1, "regular form violated (10)");
1081 switch (n
->child
->type
) {
1085 errx(1, "regular form violated (11)");
1094 punt(struct nroffopt
*nroff
, char *in
)
1097 char arg0
[32], argm
[32];
1100 warnx("punting to nroff!");
1104 (void)strlcpy(arg0
, "nroff", 32);
1113 (void)strlcpy(argm
, "-m", 32);
1114 (void)strlcat(argm
, nroff
->arg_m
, 32);
1117 args
[i
++] = "-mandoc";
1120 args
[i
++] = (char *)NULL
;
1122 (void)execvp("nroff", args
);