Annotation of mandoc/mdoc_markdown.c, Revision 1.18
1.18 ! schwarze 1: /* $Id: mdoc_markdown.c,v 1.17 2017/04/24 23:06:18 schwarze Exp $ */
1.1 schwarze 2: /*
3: * Copyright (c) 2017 Ingo Schwarze <schwarze@openbsd.org>
4: *
5: * Permission to use, copy, modify, and distribute this software for any
6: * purpose with or without fee is hereby granted, provided that the above
7: * copyright notice and this permission notice appear in all copies.
8: *
9: * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHORS DISCLAIM ALL WARRANTIES
10: * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
11: * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR
12: * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
13: * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
14: * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
15: * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
16: */
17: #include <sys/types.h>
18:
19: #include <assert.h>
20: #include <ctype.h>
21: #include <stdio.h>
1.18 ! schwarze 22: #include <stdlib.h>
1.1 schwarze 23: #include <string.h>
24:
25: #include "mandoc_aux.h"
26: #include "mandoc.h"
27: #include "roff.h"
28: #include "mdoc.h"
29: #include "main.h"
30:
31: struct md_act {
32: int (*cond)(struct roff_node *n);
33: int (*pre)(struct roff_node *n);
34: void (*post)(struct roff_node *n);
35: const char *prefix; /* pre-node string constant */
36: const char *suffix; /* post-node string constant */
37: };
38:
39: static void md_nodelist(struct roff_node *);
40: static void md_node(struct roff_node *);
41: static const char *md_stack(char c);
42: static void md_preword(void);
43: static void md_rawword(const char *);
44: static void md_word(const char *);
45: static void md_named(const char *);
46: static void md_char(unsigned char);
1.15 schwarze 47: static void md_uri(const char *);
1.1 schwarze 48:
49: static int md_cond_head(struct roff_node *);
50: static int md_cond_body(struct roff_node *);
51:
52: static int md_pre_raw(struct roff_node *);
53: static int md_pre_word(struct roff_node *);
54: static int md_pre_skip(struct roff_node *);
55: static void md_pre_syn(struct roff_node *);
1.5 schwarze 56: static int md_pre_An(struct roff_node *);
1.1 schwarze 57: static int md_pre_Ap(struct roff_node *);
58: static int md_pre_Bd(struct roff_node *);
59: static int md_pre_Bk(struct roff_node *);
60: static int md_pre_Bl(struct roff_node *);
61: static int md_pre_D1(struct roff_node *);
62: static int md_pre_Dl(struct roff_node *);
63: static int md_pre_En(struct roff_node *);
64: static int md_pre_Eo(struct roff_node *);
65: static int md_pre_Fa(struct roff_node *);
66: static int md_pre_Fd(struct roff_node *);
67: static int md_pre_Fn(struct roff_node *);
68: static int md_pre_Fo(struct roff_node *);
69: static int md_pre_In(struct roff_node *);
70: static int md_pre_It(struct roff_node *);
71: static int md_pre_Lk(struct roff_node *);
1.15 schwarze 72: static int md_pre_Mt(struct roff_node *);
1.1 schwarze 73: static int md_pre_Nd(struct roff_node *);
74: static int md_pre_Nm(struct roff_node *);
75: static int md_pre_No(struct roff_node *);
76: static int md_pre_Ns(struct roff_node *);
77: static int md_pre_Pp(struct roff_node *);
78: static int md_pre_Rs(struct roff_node *);
79: static int md_pre_Sh(struct roff_node *);
80: static int md_pre_Sm(struct roff_node *);
81: static int md_pre_Vt(struct roff_node *);
82: static int md_pre_Xr(struct roff_node *);
83: static int md_pre__T(struct roff_node *);
84: static int md_pre_br(struct roff_node *);
85:
86: static void md_post_raw(struct roff_node *);
87: static void md_post_word(struct roff_node *);
88: static void md_post_pc(struct roff_node *);
89: static void md_post_Bk(struct roff_node *);
90: static void md_post_Bl(struct roff_node *);
91: static void md_post_D1(struct roff_node *);
92: static void md_post_En(struct roff_node *);
93: static void md_post_Eo(struct roff_node *);
94: static void md_post_Fa(struct roff_node *);
95: static void md_post_Fd(struct roff_node *);
1.6 schwarze 96: static void md_post_Fl(struct roff_node *);
1.1 schwarze 97: static void md_post_Fn(struct roff_node *);
98: static void md_post_Fo(struct roff_node *);
99: static void md_post_In(struct roff_node *);
100: static void md_post_It(struct roff_node *);
101: static void md_post_Lb(struct roff_node *);
102: static void md_post_Nm(struct roff_node *);
103: static void md_post_Pf(struct roff_node *);
104: static void md_post_Vt(struct roff_node *);
105: static void md_post__T(struct roff_node *);
106:
1.17 schwarze 107: static const struct md_act __md_acts[MDOC_MAX - MDOC_Dd] = {
1.1 schwarze 108: { NULL, NULL, NULL, NULL, NULL }, /* Dd */
109: { NULL, NULL, NULL, NULL, NULL }, /* Dt */
110: { NULL, NULL, NULL, NULL, NULL }, /* Os */
111: { NULL, md_pre_Sh, NULL, NULL, NULL }, /* Sh */
112: { NULL, md_pre_Sh, NULL, NULL, NULL }, /* Ss */
113: { NULL, md_pre_Pp, NULL, NULL, NULL }, /* Pp */
114: { md_cond_body, md_pre_D1, md_post_D1, NULL, NULL }, /* D1 */
115: { md_cond_body, md_pre_Dl, md_post_D1, NULL, NULL }, /* Dl */
116: { md_cond_body, md_pre_Bd, md_post_D1, NULL, NULL }, /* Bd */
117: { NULL, NULL, NULL, NULL, NULL }, /* Ed */
118: { md_cond_body, md_pre_Bl, md_post_Bl, NULL, NULL }, /* Bl */
119: { NULL, NULL, NULL, NULL, NULL }, /* El */
120: { NULL, md_pre_It, md_post_It, NULL, NULL }, /* It */
121: { NULL, md_pre_raw, md_post_raw, "*", "*" }, /* Ad */
1.5 schwarze 122: { NULL, md_pre_An, NULL, NULL, NULL }, /* An */
1.17 schwarze 123: { NULL, md_pre_Ap, NULL, NULL, NULL }, /* Ap */
1.1 schwarze 124: { NULL, md_pre_raw, md_post_raw, "*", "*" }, /* Ar */
125: { NULL, md_pre_raw, md_post_raw, "**", "**" }, /* Cd */
126: { NULL, md_pre_raw, md_post_raw, "**", "**" }, /* Cm */
127: { NULL, md_pre_raw, md_post_raw, "`", "`" }, /* Dv */
128: { NULL, md_pre_raw, md_post_raw, "`", "`" }, /* Er */
129: { NULL, md_pre_raw, md_post_raw, "`", "`" }, /* Ev */
130: { NULL, NULL, NULL, NULL, NULL }, /* Ex */
131: { NULL, md_pre_Fa, md_post_Fa, NULL, NULL }, /* Fa */
132: { NULL, md_pre_Fd, md_post_Fd, "**", "**" }, /* Fd */
1.6 schwarze 133: { NULL, md_pre_raw, md_post_Fl, "**-", "**" }, /* Fl */
1.1 schwarze 134: { NULL, md_pre_Fn, md_post_Fn, NULL, NULL }, /* Fn */
135: { NULL, md_pre_Fd, md_post_raw, "*", "*" }, /* Ft */
136: { NULL, md_pre_raw, md_post_raw, "**", "**" }, /* Ic */
1.4 schwarze 137: { NULL, md_pre_In, md_post_In, NULL, NULL }, /* In */
1.1 schwarze 138: { NULL, md_pre_raw, md_post_raw, "`", "`" }, /* Li */
139: { md_cond_head, md_pre_Nd, NULL, NULL, NULL }, /* Nd */
140: { NULL, md_pre_Nm, md_post_Nm, "**", "**" }, /* Nm */
141: { md_cond_body, md_pre_word, md_post_word, "[", "]" }, /* Op */
142: { NULL, md_pre_Fd, md_post_raw, "*", "*" }, /* Ot */
143: { NULL, md_pre_raw, md_post_raw, "*", "*" }, /* Pa */
144: { NULL, NULL, NULL, NULL, NULL }, /* Rv */
145: { NULL, NULL, NULL, NULL, NULL }, /* St */
146: { NULL, md_pre_raw, md_post_raw, "*", "*" }, /* Va */
147: { NULL, md_pre_Vt, md_post_Vt, "*", "*" }, /* Vt */
148: { NULL, md_pre_Xr, NULL, NULL, NULL }, /* Xr */
149: { NULL, NULL, md_post_pc, NULL, NULL }, /* %A */
150: { NULL, md_pre_raw, md_post_pc, "*", "*" }, /* %B */
151: { NULL, NULL, md_post_pc, NULL, NULL }, /* %D */
152: { NULL, md_pre_raw, md_post_pc, "*", "*" }, /* %I */
153: { NULL, md_pre_raw, md_post_pc, "*", "*" }, /* %J */
154: { NULL, NULL, md_post_pc, NULL, NULL }, /* %N */
155: { NULL, NULL, md_post_pc, NULL, NULL }, /* %O */
156: { NULL, NULL, md_post_pc, NULL, NULL }, /* %P */
157: { NULL, NULL, md_post_pc, NULL, NULL }, /* %R */
158: { NULL, md_pre__T, md_post__T, NULL, NULL }, /* %T */
159: { NULL, NULL, md_post_pc, NULL, NULL }, /* %V */
160: { NULL, NULL, NULL, NULL, NULL }, /* Ac */
161: { md_cond_body, md_pre_word, md_post_word, "<", ">" }, /* Ao */
162: { md_cond_body, md_pre_word, md_post_word, "<", ">" }, /* Aq */
163: { NULL, NULL, NULL, NULL, NULL }, /* At */
164: { NULL, NULL, NULL, NULL, NULL }, /* Bc */
165: { NULL, NULL, NULL, NULL, NULL }, /* Bf XXX not implemented */
166: { md_cond_body, md_pre_word, md_post_word, "[", "]" }, /* Bo */
167: { md_cond_body, md_pre_word, md_post_word, "[", "]" }, /* Bq */
168: { NULL, NULL, NULL, NULL, NULL }, /* Bsx */
169: { NULL, NULL, NULL, NULL, NULL }, /* Bx */
170: { NULL, NULL, NULL, NULL, NULL }, /* Db */
171: { NULL, NULL, NULL, NULL, NULL }, /* Dc */
172: { md_cond_body, md_pre_word, md_post_word, "\"", "\"" }, /* Do */
173: { md_cond_body, md_pre_word, md_post_word, "\"", "\"" }, /* Dq */
174: { NULL, NULL, NULL, NULL, NULL }, /* Ec */
175: { NULL, NULL, NULL, NULL, NULL }, /* Ef */
176: { NULL, md_pre_raw, md_post_raw, "*", "*" }, /* Em */
177: { md_cond_body, md_pre_Eo, md_post_Eo, NULL, NULL }, /* Eo */
178: { NULL, NULL, NULL, NULL, NULL }, /* Fx */
179: { NULL, md_pre_raw, md_post_raw, "**", "**" }, /* Ms */
180: { NULL, md_pre_No, NULL, NULL, NULL }, /* No */
181: { NULL, md_pre_Ns, NULL, NULL, NULL }, /* Ns */
182: { NULL, NULL, NULL, NULL, NULL }, /* Nx */
183: { NULL, NULL, NULL, NULL, NULL }, /* Ox */
184: { NULL, NULL, NULL, NULL, NULL }, /* Pc */
185: { NULL, NULL, md_post_Pf, NULL, NULL }, /* Pf */
186: { md_cond_body, md_pre_word, md_post_word, "(", ")" }, /* Po */
187: { md_cond_body, md_pre_word, md_post_word, "(", ")" }, /* Pq */
188: { NULL, NULL, NULL, NULL, NULL }, /* Qc */
189: { md_cond_body, md_pre_raw, md_post_raw, "'`", "`'" }, /* Ql */
190: { md_cond_body, md_pre_word, md_post_word, "\"", "\"" }, /* Qo */
191: { md_cond_body, md_pre_word, md_post_word, "\"", "\"" }, /* Qq */
192: { NULL, NULL, NULL, NULL, NULL }, /* Re */
193: { md_cond_body, md_pre_Rs, NULL, NULL, NULL }, /* Rs */
194: { NULL, NULL, NULL, NULL, NULL }, /* Sc */
195: { md_cond_body, md_pre_word, md_post_word, "'", "'" }, /* So */
196: { md_cond_body, md_pre_word, md_post_word, "'", "'" }, /* Sq */
197: { NULL, md_pre_Sm, NULL, NULL, NULL }, /* Sm */
198: { NULL, md_pre_raw, md_post_raw, "*", "*" }, /* Sx */
199: { NULL, md_pre_raw, md_post_raw, "**", "**" }, /* Sy */
200: { NULL, md_pre_raw, md_post_raw, "`", "`" }, /* Tn */
201: { NULL, NULL, NULL, NULL, NULL }, /* Ux */
202: { NULL, NULL, NULL, NULL, NULL }, /* Xc */
203: { NULL, NULL, NULL, NULL, NULL }, /* Xo */
204: { NULL, md_pre_Fo, md_post_Fo, "**", "**" }, /* Fo */
205: { NULL, NULL, NULL, NULL, NULL }, /* Fc */
206: { md_cond_body, md_pre_word, md_post_word, "[", "]" }, /* Oo */
207: { NULL, NULL, NULL, NULL, NULL }, /* Oc */
208: { NULL, md_pre_Bk, md_post_Bk, NULL, NULL }, /* Bk */
209: { NULL, NULL, NULL, NULL, NULL }, /* Ek */
210: { NULL, NULL, NULL, NULL, NULL }, /* Bt */
211: { NULL, NULL, NULL, NULL, NULL }, /* Hf */
212: { NULL, md_pre_raw, md_post_raw, "*", "*" }, /* Fr */
213: { NULL, NULL, NULL, NULL, NULL }, /* Ud */
214: { NULL, NULL, md_post_Lb, NULL, NULL }, /* Lb */
215: { NULL, md_pre_Pp, NULL, NULL, NULL }, /* Lp */
216: { NULL, md_pre_Lk, NULL, NULL, NULL }, /* Lk */
1.15 schwarze 217: { NULL, md_pre_Mt, NULL, NULL, NULL }, /* Mt */
1.1 schwarze 218: { md_cond_body, md_pre_word, md_post_word, "{", "}" }, /* Brq */
219: { md_cond_body, md_pre_word, md_post_word, "{", "}" }, /* Bro */
220: { NULL, NULL, NULL, NULL, NULL }, /* Brc */
221: { NULL, NULL, md_post_pc, NULL, NULL }, /* %C */
222: { NULL, md_pre_skip, NULL, NULL, NULL }, /* Es */
223: { md_cond_body, md_pre_En, md_post_En, NULL, NULL }, /* En */
224: { NULL, NULL, NULL, NULL, NULL }, /* Dx */
225: { NULL, NULL, md_post_pc, NULL, NULL }, /* %Q */
226: { NULL, md_pre_Pp, NULL, NULL, NULL }, /* sp */
1.3 schwarze 227: { NULL, md_pre_Lk, md_post_pc, NULL, NULL }, /* %U */
1.1 schwarze 228: { NULL, NULL, NULL, NULL, NULL }, /* Ta */
229: { NULL, NULL, NULL, NULL, NULL }, /* ll */
230: };
1.17 schwarze 231: static const struct md_act *const md_acts = __md_acts - MDOC_Dd;
1.1 schwarze 232:
233: static int outflags;
234: #define MD_spc (1 << 0) /* Blank character before next word. */
235: #define MD_spc_force (1 << 1) /* Even before trailing punctuation. */
236: #define MD_nonl (1 << 2) /* Prevent linebreak in markdown code. */
237: #define MD_nl (1 << 3) /* Break markdown code line. */
238: #define MD_br (1 << 4) /* Insert an output line break. */
239: #define MD_sp (1 << 5) /* Insert a paragraph break. */
240: #define MD_Sm (1 << 6) /* Horizontal spacing mode. */
241: #define MD_Bk (1 << 7) /* Word keep mode. */
1.5 schwarze 242: #define MD_An_split (1 << 8) /* Author mode is "split". */
243: #define MD_An_nosplit (1 << 9) /* Author mode is "nosplit". */
1.1 schwarze 244:
245: static int escflags; /* Escape in generated markdown code: */
246: #define ESC_BOL (1 << 0) /* "#*+-" near the beginning of a line. */
247: #define ESC_NUM (1 << 1) /* "." after a leading number. */
248: #define ESC_HYP (1 << 2) /* "(" immediately after "]". */
249: #define ESC_SQU (1 << 4) /* "]" when "[" is open. */
250: #define ESC_FON (1 << 5) /* "*" immediately after unrelated "*". */
1.8 schwarze 251: #define ESC_EOL (1 << 6) /* " " at the and of a line. */
1.1 schwarze 252:
253: static int code_blocks, quote_blocks, list_blocks;
254: static int outcount;
255:
256: void
257: markdown_mdoc(void *arg, const struct roff_man *mdoc)
258: {
259: outflags = MD_Sm;
260: md_word(mdoc->meta.title);
261: if (mdoc->meta.msec != NULL) {
262: outflags &= ~MD_spc;
263: md_word("(");
264: md_word(mdoc->meta.msec);
265: md_word(")");
266: }
267: md_word("-");
268: md_word(mdoc->meta.vol);
269: if (mdoc->meta.arch != NULL) {
270: md_word("(");
271: md_word(mdoc->meta.arch);
272: md_word(")");
273: }
274: outflags |= MD_sp;
275:
276: md_nodelist(mdoc->first->child);
277:
278: outflags |= MD_sp;
279: md_word(mdoc->meta.os);
280: md_word("-");
281: md_word(mdoc->meta.date);
282: putchar('\n');
283: }
284:
285: static void
286: md_nodelist(struct roff_node *n)
287: {
288: while (n != NULL) {
289: md_node(n);
290: n = n->next;
291: }
292: }
293:
294: static void
295: md_node(struct roff_node *n)
296: {
297: const struct md_act *act;
298: int cond, process_children;
299:
300: if (n->flags & NODE_NOPRT)
301: return;
302:
303: if (outflags & MD_nonl)
304: outflags &= ~(MD_nl | MD_sp);
305: else if (outflags & MD_spc && n->flags & NODE_LINE)
306: outflags |= MD_nl;
307:
308: act = NULL;
309: cond = 0;
310: process_children = 1;
311: n->flags &= ~NODE_ENDED;
312:
1.18 ! schwarze 313: if (n->type == ROFFT_TEXT) {
1.1 schwarze 314: if (n->flags & NODE_DELIMC)
315: outflags &= ~(MD_spc | MD_spc_force);
316: else if (outflags & MD_Sm)
317: outflags |= MD_spc_force;
318: md_word(n->string);
319: if (n->flags & NODE_DELIMO)
320: outflags &= ~(MD_spc | MD_spc_force);
321: else if (outflags & MD_Sm)
322: outflags |= MD_spc;
1.18 ! schwarze 323: } else if (n->tok < ROFF_MAX) {
! 324: switch (n->tok) {
! 325: case ROFF_br:
! 326: md_pre_br(n);
! 327: break;
! 328: default:
! 329: abort();
! 330: }
! 331: } else {
! 332: assert(n->tok >= MDOC_Dd && n->tok < MDOC_MAX);
1.1 schwarze 333: act = md_acts + n->tok;
334: cond = act->cond == NULL || (*act->cond)(n);
335: if (cond && act->pre != NULL &&
336: (n->end == ENDBODY_NOT || n->child != NULL))
337: process_children = (*act->pre)(n);
338: }
339:
340: if (process_children && n->child != NULL)
341: md_nodelist(n->child);
342:
343: if (n->flags & NODE_ENDED)
344: return;
345:
346: if (cond && act->post != NULL)
347: (*act->post)(n);
348:
349: if (n->end != ENDBODY_NOT)
350: n->body->flags |= NODE_ENDED;
351: }
352:
353: static const char *
354: md_stack(char c)
355: {
356: static char *stack;
357: static size_t sz;
358: static size_t cur;
359:
360: switch (c) {
361: case '\0':
362: break;
363: case (char)-1:
364: assert(cur);
365: stack[--cur] = '\0';
366: break;
367: default:
368: if (cur + 1 >= sz) {
369: sz += 8;
370: stack = mandoc_realloc(stack, sz);
371: }
372: stack[cur] = c;
373: stack[++cur] = '\0';
374: break;
375: }
376: return stack == NULL ? "" : stack;
377: }
378:
379: /*
380: * Handle vertical and horizontal spacing.
381: */
382: static void
383: md_preword(void)
384: {
1.9 schwarze 385: const char *cp;
386:
1.1 schwarze 387: /*
388: * If a list block is nested inside a code block or a blockquote,
389: * blank lines for paragraph breaks no longer work; instead,
390: * they terminate the list. Work around this markdown issue
391: * by using mere line breaks instead.
392: */
1.8 schwarze 393:
1.1 schwarze 394: if (list_blocks && outflags & MD_sp) {
395: outflags &= ~MD_sp;
396: outflags |= MD_br;
397: }
398:
1.8 schwarze 399: /*
400: * End the old line if requested.
401: * Escape whitespace at the end of the markdown line
402: * such that it won't look like an output line break.
403: */
1.1 schwarze 404:
405: if (outflags & MD_sp)
406: putchar('\n');
407: else if (outflags & MD_br) {
408: putchar(' ');
409: putchar(' ');
1.8 schwarze 410: } else if (outflags & MD_nl && escflags & ESC_EOL)
411: md_named("zwnj");
1.1 schwarze 412:
413: /* Start a new line if necessary. */
414:
415: if (outflags & (MD_nl | MD_br | MD_sp)) {
416: putchar('\n');
1.9 schwarze 417: for (cp = md_stack('\0'); *cp != '\0'; cp++) {
418: putchar(*cp);
419: if (*cp == '>')
420: putchar(' ');
421: }
1.1 schwarze 422: outflags &= ~(MD_nl | MD_br | MD_sp);
423: escflags = ESC_BOL;
424: outcount = 0;
425:
426: /* Handle horizontal spacing. */
427:
428: } else if (outflags & MD_spc) {
429: if (outflags & MD_Bk)
430: fputs(" ", stdout);
431: else
432: putchar(' ');
433: escflags &= ~ESC_FON;
434: outcount++;
435: }
436:
437: outflags &= ~(MD_spc_force | MD_nonl);
438: if (outflags & MD_Sm)
439: outflags |= MD_spc;
440: else
441: outflags &= ~MD_spc;
442: }
443:
444: /*
445: * Print markdown syntax elements.
446: * Can also be used for constant strings when neither escaping
447: * nor delimiter handling is required.
448: */
449: static void
450: md_rawword(const char *s)
451: {
452: md_preword();
453:
1.8 schwarze 454: if (*s == '\0')
1.1 schwarze 455: return;
456:
457: if (escflags & ESC_FON) {
458: escflags &= ~ESC_FON;
459: if (*s == '*' && !code_blocks)
460: fputs("‌", stdout);
461: }
462:
463: while (*s != '\0') {
464: switch(*s) {
465: case '*':
466: if (s[1] == '\0')
467: escflags |= ESC_FON;
468: break;
469: case '[':
470: escflags |= ESC_SQU;
471: break;
472: case ']':
473: escflags |= ESC_HYP;
474: escflags &= ~ESC_SQU;
475: break;
476: default:
477: break;
478: }
479: md_char(*s++);
480: }
1.8 schwarze 481: if (s[-1] == ' ')
482: escflags |= ESC_EOL;
483: else
484: escflags &= ~ESC_EOL;
1.1 schwarze 485: }
486:
487: /*
488: * Print text and mdoc(7) syntax elements.
489: */
490: static void
491: md_word(const char *s)
492: {
493: const char *seq, *prevfont, *currfont, *nextfont;
494: char c;
495: int bs, sz, uc;
496:
497: /* No spacing before closing delimiters. */
498: if (s[0] != '\0' && s[1] == '\0' &&
499: strchr("!),.:;?]", s[0]) != NULL &&
500: (outflags & MD_spc_force) == 0)
501: outflags &= ~MD_spc;
502:
503: md_preword();
504:
1.8 schwarze 505: if (*s == '\0')
506: return;
507:
1.1 schwarze 508: /* No spacing after opening delimiters. */
509: if ((s[0] == '(' || s[0] == '[') && s[1] == '\0')
510: outflags &= ~MD_spc;
511:
512: prevfont = currfont = "";
513: while ((c = *s++) != '\0') {
514: bs = 0;
515: switch(c) {
516: case ASCII_NBRSP:
517: if (code_blocks)
518: c = ' ';
519: else {
520: md_named("nbsp");
521: c = '\0';
522: }
523: break;
524: case ASCII_HYPH:
525: bs = escflags & ESC_BOL && !code_blocks;
526: c = '-';
527: break;
528: case ASCII_BREAK:
529: continue;
530: case '#':
531: case '+':
532: case '-':
533: bs = escflags & ESC_BOL && !code_blocks;
534: break;
535: case '(':
536: bs = escflags & ESC_HYP && !code_blocks;
537: break;
538: case ')':
1.14 schwarze 539: bs = escflags & ESC_NUM && !code_blocks;
1.1 schwarze 540: break;
541: case '*':
542: case '[':
543: case '_':
544: case '`':
545: bs = !code_blocks;
546: break;
547: case '.':
548: bs = escflags & ESC_NUM && !code_blocks;
549: break;
550: case '<':
551: if (code_blocks == 0) {
552: md_named("lt");
553: c = '\0';
554: }
555: break;
556: case '=':
557: if (escflags & ESC_BOL && !code_blocks) {
558: md_named("equals");
559: c = '\0';
560: }
561: break;
562: case '>':
563: if (code_blocks == 0) {
564: md_named("gt");
565: c = '\0';
566: }
567: break;
568: case '\\':
569: uc = 0;
570: nextfont = NULL;
571: switch (mandoc_escape(&s, &seq, &sz)) {
572: case ESCAPE_UNICODE:
573: uc = mchars_num2uc(seq + 1, sz - 1);
574: break;
575: case ESCAPE_NUMBERED:
576: uc = mchars_num2char(seq, sz);
577: break;
578: case ESCAPE_SPECIAL:
579: uc = mchars_spec2cp(seq, sz);
580: break;
581: case ESCAPE_FONTBOLD:
582: nextfont = "**";
583: break;
584: case ESCAPE_FONTITALIC:
585: nextfont = "*";
586: break;
587: case ESCAPE_FONTBI:
588: nextfont = "***";
589: break;
590: case ESCAPE_FONT:
591: case ESCAPE_FONTROMAN:
592: nextfont = "";
593: break;
594: case ESCAPE_FONTPREV:
595: nextfont = prevfont;
596: break;
597: case ESCAPE_NOSPACE:
598: case ESCAPE_SKIPCHAR:
599: case ESCAPE_OVERSTRIKE:
600: /* XXX not implemented */
601: /* FALLTHROUGH */
602: case ESCAPE_ERROR:
603: default:
604: break;
605: }
606: if (nextfont != NULL && !code_blocks) {
607: if (*currfont != '\0') {
608: outflags &= ~MD_spc;
609: md_rawword(currfont);
610: }
611: prevfont = currfont;
612: currfont = nextfont;
613: if (*currfont != '\0') {
614: outflags &= ~MD_spc;
615: md_rawword(currfont);
616: }
617: }
618: if (uc) {
619: if ((uc < 0x20 && uc != 0x09) ||
620: (uc > 0x7E && uc < 0xA0))
621: uc = 0xFFFD;
622: if (code_blocks) {
623: seq = mchars_uc2str(uc);
624: fputs(seq, stdout);
625: outcount += strlen(seq);
626: } else {
627: printf("&#%d;", uc);
628: outcount++;
629: }
630: escflags &= ~ESC_FON;
631: }
632: c = '\0';
633: break;
634: case ']':
635: bs = escflags & ESC_SQU && !code_blocks;
636: escflags |= ESC_HYP;
637: break;
638: default:
639: break;
640: }
641: if (bs)
642: putchar('\\');
643: md_char(c);
644: }
645: if (*currfont != '\0') {
646: outflags &= ~MD_spc;
647: md_rawword(currfont);
1.8 schwarze 648: } else if (s[-2] == ' ')
649: escflags |= ESC_EOL;
650: else
651: escflags &= ~ESC_EOL;
1.1 schwarze 652: }
653:
654: /*
655: * Print a single HTML named character reference.
656: */
657: static void
658: md_named(const char *s)
659: {
660: printf("&%s;", s);
1.8 schwarze 661: escflags &= ~(ESC_FON | ESC_EOL);
1.1 schwarze 662: outcount++;
663: }
664:
665: /*
666: * Print a single raw character and maintain certain escape flags.
667: */
668: static void
669: md_char(unsigned char c)
670: {
671: if (c != '\0') {
672: putchar(c);
673: if (c == '*')
674: escflags |= ESC_FON;
675: else
676: escflags &= ~ESC_FON;
677: outcount++;
678: }
679: if (c != ']')
680: escflags &= ~ESC_HYP;
681: if (c == ' ' || c == '\t' || c == '>')
682: return;
683: if (isdigit(c) == 0)
684: escflags &= ~ESC_NUM;
685: else if (escflags & ESC_BOL)
686: escflags |= ESC_NUM;
687: escflags &= ~ESC_BOL;
688: }
689:
690: static int
691: md_cond_head(struct roff_node *n)
692: {
693: return n->type == ROFFT_HEAD;
694: }
695:
696: static int
697: md_cond_body(struct roff_node *n)
698: {
699: return n->type == ROFFT_BODY;
700: }
701:
702: static int
703: md_pre_raw(struct roff_node *n)
704: {
705: const char *prefix;
706:
707: if ((prefix = md_acts[n->tok].prefix) != NULL) {
708: md_rawword(prefix);
709: outflags &= ~MD_spc;
1.13 schwarze 710: if (*prefix == '`')
711: code_blocks++;
1.1 schwarze 712: }
713: return 1;
714: }
715:
716: static void
717: md_post_raw(struct roff_node *n)
718: {
719: const char *suffix;
720:
721: if ((suffix = md_acts[n->tok].suffix) != NULL) {
722: outflags &= ~(MD_spc | MD_nl);
723: md_rawword(suffix);
1.13 schwarze 724: if (*suffix == '`')
725: code_blocks--;
1.1 schwarze 726: }
727: }
728:
729: static int
730: md_pre_word(struct roff_node *n)
731: {
732: const char *prefix;
733:
734: if ((prefix = md_acts[n->tok].prefix) != NULL) {
735: md_word(prefix);
736: outflags &= ~MD_spc;
737: }
738: return 1;
739: }
740:
741: static void
742: md_post_word(struct roff_node *n)
743: {
744: const char *suffix;
745:
746: if ((suffix = md_acts[n->tok].suffix) != NULL) {
747: outflags &= ~(MD_spc | MD_nl);
748: md_word(suffix);
749: }
750: }
751:
752: static void
753: md_post_pc(struct roff_node *n)
754: {
755: md_post_raw(n);
756: if (n->parent->tok != MDOC_Rs)
757: return;
758: if (n->next != NULL) {
759: md_word(",");
760: if (n->prev != NULL &&
761: n->prev->tok == n->tok &&
762: n->next->tok == n->tok)
763: md_word("and");
764: } else {
765: md_word(".");
766: outflags |= MD_nl;
767: }
768: }
769:
770: static int
771: md_pre_skip(struct roff_node *n)
772: {
773: return 0;
774: }
775:
776: static void
777: md_pre_syn(struct roff_node *n)
778: {
779: if (n->prev == NULL || ! (n->flags & NODE_SYNPRETTY))
780: return;
781:
782: if (n->prev->tok == n->tok &&
783: n->tok != MDOC_Ft &&
784: n->tok != MDOC_Fo &&
785: n->tok != MDOC_Fn) {
786: outflags |= MD_br;
787: return;
788: }
789:
790: switch (n->prev->tok) {
791: case MDOC_Fd:
792: case MDOC_Fn:
793: case MDOC_Fo:
794: case MDOC_In:
795: case MDOC_Vt:
796: outflags |= MD_sp;
797: break;
798: case MDOC_Ft:
799: if (n->tok != MDOC_Fn && n->tok != MDOC_Fo) {
800: outflags |= MD_sp;
801: break;
802: }
803: /* FALLTHROUGH */
804: default:
805: outflags |= MD_br;
806: break;
807: }
808: }
809:
810: static int
1.5 schwarze 811: md_pre_An(struct roff_node *n)
812: {
813: switch (n->norm->An.auth) {
814: case AUTH_split:
815: outflags &= ~MD_An_nosplit;
816: outflags |= MD_An_split;
817: return 0;
818: case AUTH_nosplit:
819: outflags &= ~MD_An_split;
820: outflags |= MD_An_nosplit;
821: return 0;
822: default:
823: if (outflags & MD_An_split)
824: outflags |= MD_br;
825: else if (n->sec == SEC_AUTHORS &&
826: ! (outflags & MD_An_nosplit))
827: outflags |= MD_An_split;
828: return 1;
829: }
830: }
831:
832: static int
1.1 schwarze 833: md_pre_Ap(struct roff_node *n)
834: {
835: outflags &= ~MD_spc;
836: md_word("'");
837: outflags &= ~MD_spc;
838: return 0;
839: }
840:
841: static int
842: md_pre_Bd(struct roff_node *n)
843: {
844: switch (n->norm->Bd.type) {
845: case DISP_unfilled:
846: case DISP_literal:
847: return md_pre_Dl(n);
848: default:
849: return md_pre_D1(n);
850: }
851: }
852:
853: static int
854: md_pre_Bk(struct roff_node *n)
855: {
856: switch (n->type) {
857: case ROFFT_BLOCK:
858: return 1;
859: case ROFFT_BODY:
860: outflags |= MD_Bk;
861: return 1;
862: default:
863: return 0;
864: }
865: }
866:
867: static void
868: md_post_Bk(struct roff_node *n)
869: {
870: if (n->type == ROFFT_BODY)
871: outflags &= ~MD_Bk;
872: }
873:
874: static int
875: md_pre_Bl(struct roff_node *n)
876: {
877: n->norm->Bl.count = 0;
878: if (n->norm->Bl.type == LIST_column)
879: md_pre_Dl(n);
880: outflags |= MD_sp;
881: return 1;
882: }
883:
884: static void
885: md_post_Bl(struct roff_node *n)
886: {
887: n->norm->Bl.count = 0;
888: if (n->norm->Bl.type == LIST_column)
889: md_post_D1(n);
890: outflags |= MD_sp;
891: }
892:
893: static int
894: md_pre_D1(struct roff_node *n)
895: {
896: /*
897: * Markdown blockquote syntax does not work inside code blocks.
898: * The best we can do is fall back to another nested code block.
899: */
900: if (code_blocks) {
901: md_stack('\t');
902: code_blocks++;
903: } else {
904: md_stack('>');
905: quote_blocks++;
906: }
907: outflags |= MD_sp;
908: return 1;
909: }
910:
911: static void
912: md_post_D1(struct roff_node *n)
913: {
914: md_stack((char)-1);
915: if (code_blocks)
916: code_blocks--;
917: else
918: quote_blocks--;
919: outflags |= MD_sp;
920: }
921:
922: static int
923: md_pre_Dl(struct roff_node *n)
924: {
925: /*
926: * Markdown code block syntax does not work inside blockquotes.
927: * The best we can do is fall back to another nested blockquote.
928: */
929: if (quote_blocks) {
930: md_stack('>');
931: quote_blocks++;
932: } else {
933: md_stack('\t');
934: code_blocks++;
935: }
936: outflags |= MD_sp;
937: return 1;
938: }
939:
940: static int
941: md_pre_En(struct roff_node *n)
942: {
943: if (n->norm->Es == NULL ||
944: n->norm->Es->child == NULL)
945: return 1;
946:
947: md_word(n->norm->Es->child->string);
948: outflags &= ~MD_spc;
949: return 1;
950: }
951:
952: static void
953: md_post_En(struct roff_node *n)
954: {
955: if (n->norm->Es == NULL ||
956: n->norm->Es->child == NULL ||
957: n->norm->Es->child->next == NULL)
958: return;
959:
960: outflags &= ~MD_spc;
961: md_word(n->norm->Es->child->next->string);
962: }
963:
964: static int
965: md_pre_Eo(struct roff_node *n)
966: {
967: if (n->end == ENDBODY_NOT &&
968: n->parent->head->child == NULL &&
969: n->child != NULL &&
970: n->child->end != ENDBODY_NOT)
971: md_preword();
972: else if (n->end != ENDBODY_NOT ? n->child != NULL :
973: n->parent->head->child != NULL && (n->child != NULL ||
974: (n->parent->tail != NULL && n->parent->tail->child != NULL)))
975: outflags &= ~(MD_spc | MD_nl);
976: return 1;
977: }
978:
979: static void
980: md_post_Eo(struct roff_node *n)
981: {
982: if (n->end != ENDBODY_NOT) {
983: outflags |= MD_spc;
984: return;
985: }
986:
1.7 schwarze 987: if (n->child == NULL && n->parent->head->child == NULL)
988: return;
1.1 schwarze 989:
1.7 schwarze 990: if (n->parent->tail != NULL && n->parent->tail->child != NULL)
1.1 schwarze 991: outflags &= ~MD_spc;
1.7 schwarze 992: else
1.1 schwarze 993: outflags |= MD_spc;
994: }
995:
996: static int
997: md_pre_Fa(struct roff_node *n)
998: {
999: int am_Fa;
1000:
1001: am_Fa = n->tok == MDOC_Fa;
1002:
1003: if (am_Fa)
1004: n = n->child;
1005:
1006: while (n != NULL) {
1007: md_rawword("*");
1008: outflags &= ~MD_spc;
1009: md_node(n);
1010: outflags &= ~MD_spc;
1011: md_rawword("*");
1012: if ((n = n->next) != NULL)
1013: md_word(",");
1014: }
1015: return 0;
1016: }
1017:
1018: static void
1019: md_post_Fa(struct roff_node *n)
1020: {
1021: if (n->next != NULL && n->next->tok == MDOC_Fa)
1022: md_word(",");
1023: }
1024:
1025: static int
1026: md_pre_Fd(struct roff_node *n)
1027: {
1028: md_pre_syn(n);
1029: md_pre_raw(n);
1030: return 1;
1031: }
1032:
1033: static void
1034: md_post_Fd(struct roff_node *n)
1035: {
1036: md_post_raw(n);
1037: outflags |= MD_br;
1.6 schwarze 1038: }
1039:
1040: static void
1041: md_post_Fl(struct roff_node *n)
1042: {
1043: md_post_raw(n);
1044: if (n->child == NULL && n->next != NULL &&
1045: n->next->type != ROFFT_TEXT && !(n->next->flags & NODE_LINE))
1046: outflags &= ~MD_spc;
1.1 schwarze 1047: }
1048:
1049: static int
1050: md_pre_Fn(struct roff_node *n)
1051: {
1052: md_pre_syn(n);
1053:
1054: if ((n = n->child) == NULL)
1055: return 0;
1056:
1057: md_rawword("**");
1058: outflags &= ~MD_spc;
1059: md_node(n);
1060: outflags &= ~MD_spc;
1061: md_rawword("**");
1062: outflags &= ~MD_spc;
1063: md_word("(");
1064:
1065: if ((n = n->next) != NULL)
1066: md_pre_Fa(n);
1067: return 0;
1068: }
1069:
1070: static void
1071: md_post_Fn(struct roff_node *n)
1072: {
1073: md_word(")");
1074: if (n->flags & NODE_SYNPRETTY) {
1075: md_word(";");
1076: outflags |= MD_sp;
1077: }
1078: }
1079:
1080: static int
1081: md_pre_Fo(struct roff_node *n)
1082: {
1083: switch (n->type) {
1084: case ROFFT_BLOCK:
1085: md_pre_syn(n);
1086: break;
1087: case ROFFT_HEAD:
1088: if (n->child == NULL)
1089: return 0;
1090: md_pre_raw(n);
1091: break;
1092: case ROFFT_BODY:
1093: outflags &= ~(MD_spc | MD_nl);
1094: md_word("(");
1095: break;
1096: default:
1097: break;
1098: }
1099: return 1;
1100: }
1101:
1102: static void
1103: md_post_Fo(struct roff_node *n)
1104: {
1105: switch (n->type) {
1106: case ROFFT_HEAD:
1107: if (n->child != NULL)
1108: md_post_raw(n);
1109: break;
1110: case ROFFT_BODY:
1111: md_post_Fn(n);
1112: break;
1113: default:
1114: break;
1115: }
1116: }
1117:
1118: static int
1119: md_pre_In(struct roff_node *n)
1120: {
1121: if (n->flags & NODE_SYNPRETTY) {
1122: md_pre_syn(n);
1.4 schwarze 1123: md_rawword("**");
1.1 schwarze 1124: outflags &= ~MD_spc;
1125: md_word("#include <");
1126: } else {
1127: md_word("<");
1128: outflags &= ~MD_spc;
1.4 schwarze 1129: md_rawword("*");
1.1 schwarze 1130: }
1.4 schwarze 1131: outflags &= ~MD_spc;
1.1 schwarze 1132: return 1;
1133: }
1134:
1135: static void
1136: md_post_In(struct roff_node *n)
1137: {
1138: if (n->flags & NODE_SYNPRETTY) {
1139: outflags &= ~MD_spc;
1.4 schwarze 1140: md_rawword(">**");
1.1 schwarze 1141: outflags |= MD_nl;
1142: } else {
1143: outflags &= ~MD_spc;
1.4 schwarze 1144: md_rawword("*>");
1.1 schwarze 1145: }
1146: }
1147:
1148: static int
1149: md_pre_It(struct roff_node *n)
1150: {
1151: struct roff_node *bln;
1152:
1153: switch (n->type) {
1154: case ROFFT_BLOCK:
1155: return 1;
1156:
1157: case ROFFT_HEAD:
1158: bln = n->parent->parent;
1.10 schwarze 1159: if (bln->norm->Bl.comp == 0 &&
1160: bln->norm->Bl.type != LIST_column)
1.1 schwarze 1161: outflags |= MD_sp;
1162: outflags |= MD_nl;
1163:
1164: switch (bln->norm->Bl.type) {
1165: case LIST_item:
1166: outflags |= MD_br;
1167: return 0;
1168: case LIST_inset:
1169: case LIST_diag:
1170: case LIST_ohang:
1171: outflags |= MD_br;
1172: return 1;
1173: case LIST_tag:
1174: case LIST_hang:
1175: outflags |= MD_sp;
1176: return 1;
1177: case LIST_bullet:
1178: md_rawword("*\t");
1179: break;
1180: case LIST_dash:
1181: case LIST_hyphen:
1182: md_rawword("-\t");
1183: break;
1184: case LIST_enum:
1185: md_preword();
1.11 schwarze 1186: if (bln->norm->Bl.count < 99)
1187: bln->norm->Bl.count++;
1188: printf("%d.\t", bln->norm->Bl.count);
1.1 schwarze 1189: escflags &= ~ESC_FON;
1190: break;
1.10 schwarze 1191: case LIST_column:
1192: outflags |= MD_br;
1193: return 0;
1.1 schwarze 1194: default:
1195: return 0;
1196: }
1197: outflags &= ~MD_spc;
1198: outflags |= MD_nonl;
1199: outcount = 0;
1200: md_stack('\t');
1201: if (code_blocks || quote_blocks)
1202: list_blocks++;
1203: return 0;
1204:
1205: case ROFFT_BODY:
1206: bln = n->parent->parent;
1207: switch (bln->norm->Bl.type) {
1208: case LIST_ohang:
1209: outflags |= MD_br;
1210: break;
1211: case LIST_tag:
1212: case LIST_hang:
1213: md_pre_D1(n);
1214: break;
1215: default:
1216: break;
1217: }
1218: return 1;
1219:
1220: default:
1221: return 0;
1222: }
1223: }
1224:
1225: static void
1226: md_post_It(struct roff_node *n)
1227: {
1228: struct roff_node *bln;
1229: int i, nc;
1230:
1231: if (n->type != ROFFT_BODY)
1232: return;
1233:
1234: bln = n->parent->parent;
1235: switch (bln->norm->Bl.type) {
1236: case LIST_bullet:
1237: case LIST_dash:
1238: case LIST_hyphen:
1239: case LIST_enum:
1240: md_stack((char)-1);
1241: if (code_blocks || quote_blocks)
1242: list_blocks--;
1243: break;
1244: case LIST_tag:
1245: case LIST_hang:
1246: md_post_D1(n);
1247: break;
1248:
1249: case LIST_column:
1250: if (n->next == NULL)
1251: break;
1252:
1253: /* Calculate the array index of the current column. */
1254:
1255: i = 0;
1256: while ((n = n->prev) != NULL && n->type != ROFFT_HEAD)
1257: i++;
1258:
1259: /*
1260: * If a width was specified for this column,
1261: * subtract what printed, and
1262: * add the same spacing as in mdoc_term.c.
1263: */
1264:
1265: nc = bln->norm->Bl.ncols;
1266: i = i < nc ? strlen(bln->norm->Bl.cols[i]) - outcount +
1267: (nc < 5 ? 4 : nc == 5 ? 3 : 1) : 1;
1268: if (i < 1)
1269: i = 1;
1270: while (i-- > 0)
1271: putchar(' ');
1272:
1273: outflags &= ~MD_spc;
1274: escflags &= ~ESC_FON;
1275: outcount = 0;
1276: break;
1277:
1278: default:
1279: break;
1280: }
1281: }
1282:
1283: static void
1284: md_post_Lb(struct roff_node *n)
1285: {
1286: if (n->sec == SEC_LIBRARY)
1287: outflags |= MD_br;
1288: }
1289:
1.15 schwarze 1290: static void
1291: md_uri(const char *s)
1292: {
1293: while (*s != '\0') {
1294: if (strchr("%()<>", *s) != NULL) {
1295: printf("%%%2.2hhX", *s);
1296: outcount += 3;
1297: } else {
1298: putchar(*s);
1299: outcount++;
1300: }
1301: s++;
1302: }
1303: }
1304:
1.1 schwarze 1305: static int
1306: md_pre_Lk(struct roff_node *n)
1307: {
1308: const struct roff_node *link, *descr;
1309:
1310: if ((link = n->child) == NULL)
1311: return 0;
1312:
1.16 schwarze 1313: /* Link text. */
1314: descr = link->next;
1315: if (descr == NULL || descr->flags & NODE_DELIMC)
1316: descr = link; /* no text */
1.15 schwarze 1317: md_rawword("[");
1318: outflags &= ~MD_spc;
1319: do {
1320: md_word(descr->string);
1.16 schwarze 1321: descr = descr->next;
1322: } while (descr != NULL && !(descr->flags & NODE_DELIMC));
1.15 schwarze 1323: outflags &= ~MD_spc;
1.16 schwarze 1324:
1325: /* Link target. */
1.15 schwarze 1326: md_rawword("](");
1327: md_uri(link->string);
1328: outflags &= ~MD_spc;
1329: md_rawword(")");
1.16 schwarze 1330:
1331: /* Trailing punctuation. */
1332: while (descr != NULL) {
1333: md_word(descr->string);
1334: descr = descr->next;
1335: }
1.15 schwarze 1336: return 0;
1337: }
1338:
1339: static int
1340: md_pre_Mt(struct roff_node *n)
1341: {
1342: const struct roff_node *nch;
1.1 schwarze 1343:
1.15 schwarze 1344: md_rawword("[");
1345: outflags &= ~MD_spc;
1346: for (nch = n->child; nch != NULL; nch = nch->next)
1347: md_word(nch->string);
1348: outflags &= ~MD_spc;
1349: md_rawword("](mailto:");
1350: for (nch = n->child; nch != NULL; nch = nch->next) {
1351: md_uri(nch->string);
1352: if (nch->next != NULL) {
1353: putchar(' ');
1.3 schwarze 1354: outcount++;
1355: }
1356: }
1.1 schwarze 1357: outflags &= ~MD_spc;
1.15 schwarze 1358: md_rawword(")");
1.1 schwarze 1359: return 0;
1360: }
1361:
1362: static int
1363: md_pre_Nd(struct roff_node *n)
1364: {
1365: outflags &= ~MD_nl;
1366: outflags |= MD_spc;
1367: md_word("-");
1368: return 1;
1369: }
1370:
1371: static int
1372: md_pre_Nm(struct roff_node *n)
1373: {
1374: switch (n->type) {
1375: case ROFFT_BLOCK:
1376: outflags |= MD_Bk;
1377: md_pre_syn(n);
1378: break;
1379: case ROFFT_HEAD:
1380: case ROFFT_ELEM:
1381: md_pre_raw(n);
1382: break;
1383: default:
1384: break;
1385: }
1386: return 1;
1387: }
1388:
1389: static void
1390: md_post_Nm(struct roff_node *n)
1391: {
1392: switch (n->type) {
1393: case ROFFT_BLOCK:
1394: outflags &= ~MD_Bk;
1395: break;
1396: case ROFFT_HEAD:
1397: case ROFFT_ELEM:
1398: md_post_raw(n);
1399: break;
1400: default:
1401: break;
1402: }
1403: }
1404:
1405: static int
1406: md_pre_No(struct roff_node *n)
1407: {
1408: outflags |= MD_spc_force;
1409: return 1;
1410: }
1411:
1412: static int
1413: md_pre_Ns(struct roff_node *n)
1414: {
1415: outflags &= ~MD_spc;
1416: return 0;
1417: }
1418:
1419: static void
1420: md_post_Pf(struct roff_node *n)
1421: {
1422: if (n->next != NULL && (n->next->flags & NODE_LINE) == 0)
1423: outflags &= ~MD_spc;
1424: }
1425:
1426: static int
1427: md_pre_Pp(struct roff_node *n)
1428: {
1429: outflags |= MD_sp;
1430: return 0;
1431: }
1432:
1433: static int
1434: md_pre_Rs(struct roff_node *n)
1435: {
1436: if (n->sec == SEC_SEE_ALSO)
1437: outflags |= MD_sp;
1438: return 1;
1439: }
1440:
1441: static int
1442: md_pre_Sh(struct roff_node *n)
1443: {
1444: switch (n->type) {
1.5 schwarze 1445: case ROFFT_BLOCK:
1446: if (n->sec == SEC_AUTHORS)
1447: outflags &= ~(MD_An_split | MD_An_nosplit);
1448: break;
1.1 schwarze 1449: case ROFFT_HEAD:
1450: outflags |= MD_sp;
1451: md_rawword(n->tok == MDOC_Sh ? "#" : "##");
1452: break;
1453: case ROFFT_BODY:
1454: outflags |= MD_sp;
1455: break;
1456: default:
1457: break;
1458: }
1459: return 1;
1460: }
1461:
1462: static int
1463: md_pre_Sm(struct roff_node *n)
1464: {
1465: if (n->child == NULL)
1466: outflags ^= MD_Sm;
1467: else if (strcmp("on", n->child->string) == 0)
1468: outflags |= MD_Sm;
1469: else
1470: outflags &= ~MD_Sm;
1471:
1472: if (outflags & MD_Sm)
1473: outflags |= MD_spc;
1474:
1475: return 0;
1476: }
1477:
1478: static int
1479: md_pre_Vt(struct roff_node *n)
1480: {
1481: switch (n->type) {
1482: case ROFFT_BLOCK:
1483: md_pre_syn(n);
1484: return 1;
1485: case ROFFT_BODY:
1486: case ROFFT_ELEM:
1487: md_pre_raw(n);
1488: return 1;
1489: default:
1490: return 0;
1491: }
1492: }
1493:
1494: static void
1495: md_post_Vt(struct roff_node *n)
1496: {
1497: switch (n->type) {
1498: case ROFFT_BODY:
1499: case ROFFT_ELEM:
1500: md_post_raw(n);
1501: break;
1502: default:
1503: break;
1504: }
1505: }
1506:
1507: static int
1508: md_pre_Xr(struct roff_node *n)
1509: {
1510: n = n->child;
1511: if (n == NULL)
1512: return 0;
1513: md_node(n);
1514: n = n->next;
1515: if (n == NULL)
1516: return 0;
1517: outflags &= ~MD_spc;
1518: md_word("(");
1519: md_node(n);
1520: md_word(")");
1521: return 0;
1522: }
1523:
1524: static int
1525: md_pre__T(struct roff_node *n)
1526: {
1.2 schwarze 1527: if (n->parent->tok == MDOC_Rs && n->parent->norm->Rs.quote_T)
1.1 schwarze 1528: md_word("\"");
1529: else
1530: md_rawword("*");
1531: outflags &= ~MD_spc;
1532: return 1;
1533: }
1534:
1535: static void
1536: md_post__T(struct roff_node *n)
1537: {
1538: outflags &= ~MD_spc;
1.2 schwarze 1539: if (n->parent->tok == MDOC_Rs && n->parent->norm->Rs.quote_T)
1.1 schwarze 1540: md_word("\"");
1541: else
1542: md_rawword("*");
1543: md_post_pc(n);
1544: }
1545:
1546: static int
1547: md_pre_br(struct roff_node *n)
1548: {
1549: outflags |= MD_br;
1550: return 0;
1551: }
CVSweb