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