Annotation of mandoc/mdoc_markdown.c, Revision 1.19
1.19 ! schwarze 1: /* $Id: mdoc_markdown.c,v 1.18 2017/05/04 17:48:29 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:
1.19 ! schwarze 326: process_children = md_pre_br(n);
! 327: break;
! 328: case ROFF_ft:
! 329: process_children = 0;
1.18 schwarze 330: break;
331: default:
332: abort();
333: }
334: } else {
335: assert(n->tok >= MDOC_Dd && n->tok < MDOC_MAX);
1.1 schwarze 336: act = md_acts + n->tok;
337: cond = act->cond == NULL || (*act->cond)(n);
338: if (cond && act->pre != NULL &&
339: (n->end == ENDBODY_NOT || n->child != NULL))
340: process_children = (*act->pre)(n);
341: }
342:
343: if (process_children && n->child != NULL)
344: md_nodelist(n->child);
345:
346: if (n->flags & NODE_ENDED)
347: return;
348:
349: if (cond && act->post != NULL)
350: (*act->post)(n);
351:
352: if (n->end != ENDBODY_NOT)
353: n->body->flags |= NODE_ENDED;
354: }
355:
356: static const char *
357: md_stack(char c)
358: {
359: static char *stack;
360: static size_t sz;
361: static size_t cur;
362:
363: switch (c) {
364: case '\0':
365: break;
366: case (char)-1:
367: assert(cur);
368: stack[--cur] = '\0';
369: break;
370: default:
371: if (cur + 1 >= sz) {
372: sz += 8;
373: stack = mandoc_realloc(stack, sz);
374: }
375: stack[cur] = c;
376: stack[++cur] = '\0';
377: break;
378: }
379: return stack == NULL ? "" : stack;
380: }
381:
382: /*
383: * Handle vertical and horizontal spacing.
384: */
385: static void
386: md_preword(void)
387: {
1.9 schwarze 388: const char *cp;
389:
1.1 schwarze 390: /*
391: * If a list block is nested inside a code block or a blockquote,
392: * blank lines for paragraph breaks no longer work; instead,
393: * they terminate the list. Work around this markdown issue
394: * by using mere line breaks instead.
395: */
1.8 schwarze 396:
1.1 schwarze 397: if (list_blocks && outflags & MD_sp) {
398: outflags &= ~MD_sp;
399: outflags |= MD_br;
400: }
401:
1.8 schwarze 402: /*
403: * End the old line if requested.
404: * Escape whitespace at the end of the markdown line
405: * such that it won't look like an output line break.
406: */
1.1 schwarze 407:
408: if (outflags & MD_sp)
409: putchar('\n');
410: else if (outflags & MD_br) {
411: putchar(' ');
412: putchar(' ');
1.8 schwarze 413: } else if (outflags & MD_nl && escflags & ESC_EOL)
414: md_named("zwnj");
1.1 schwarze 415:
416: /* Start a new line if necessary. */
417:
418: if (outflags & (MD_nl | MD_br | MD_sp)) {
419: putchar('\n');
1.9 schwarze 420: for (cp = md_stack('\0'); *cp != '\0'; cp++) {
421: putchar(*cp);
422: if (*cp == '>')
423: putchar(' ');
424: }
1.1 schwarze 425: outflags &= ~(MD_nl | MD_br | MD_sp);
426: escflags = ESC_BOL;
427: outcount = 0;
428:
429: /* Handle horizontal spacing. */
430:
431: } else if (outflags & MD_spc) {
432: if (outflags & MD_Bk)
433: fputs(" ", stdout);
434: else
435: putchar(' ');
436: escflags &= ~ESC_FON;
437: outcount++;
438: }
439:
440: outflags &= ~(MD_spc_force | MD_nonl);
441: if (outflags & MD_Sm)
442: outflags |= MD_spc;
443: else
444: outflags &= ~MD_spc;
445: }
446:
447: /*
448: * Print markdown syntax elements.
449: * Can also be used for constant strings when neither escaping
450: * nor delimiter handling is required.
451: */
452: static void
453: md_rawword(const char *s)
454: {
455: md_preword();
456:
1.8 schwarze 457: if (*s == '\0')
1.1 schwarze 458: return;
459:
460: if (escflags & ESC_FON) {
461: escflags &= ~ESC_FON;
462: if (*s == '*' && !code_blocks)
463: fputs("‌", stdout);
464: }
465:
466: while (*s != '\0') {
467: switch(*s) {
468: case '*':
469: if (s[1] == '\0')
470: escflags |= ESC_FON;
471: break;
472: case '[':
473: escflags |= ESC_SQU;
474: break;
475: case ']':
476: escflags |= ESC_HYP;
477: escflags &= ~ESC_SQU;
478: break;
479: default:
480: break;
481: }
482: md_char(*s++);
483: }
1.8 schwarze 484: if (s[-1] == ' ')
485: escflags |= ESC_EOL;
486: else
487: escflags &= ~ESC_EOL;
1.1 schwarze 488: }
489:
490: /*
491: * Print text and mdoc(7) syntax elements.
492: */
493: static void
494: md_word(const char *s)
495: {
496: const char *seq, *prevfont, *currfont, *nextfont;
497: char c;
498: int bs, sz, uc;
499:
500: /* No spacing before closing delimiters. */
501: if (s[0] != '\0' && s[1] == '\0' &&
502: strchr("!),.:;?]", s[0]) != NULL &&
503: (outflags & MD_spc_force) == 0)
504: outflags &= ~MD_spc;
505:
506: md_preword();
507:
1.8 schwarze 508: if (*s == '\0')
509: return;
510:
1.1 schwarze 511: /* No spacing after opening delimiters. */
512: if ((s[0] == '(' || s[0] == '[') && s[1] == '\0')
513: outflags &= ~MD_spc;
514:
515: prevfont = currfont = "";
516: while ((c = *s++) != '\0') {
517: bs = 0;
518: switch(c) {
519: case ASCII_NBRSP:
520: if (code_blocks)
521: c = ' ';
522: else {
523: md_named("nbsp");
524: c = '\0';
525: }
526: break;
527: case ASCII_HYPH:
528: bs = escflags & ESC_BOL && !code_blocks;
529: c = '-';
530: break;
531: case ASCII_BREAK:
532: continue;
533: case '#':
534: case '+':
535: case '-':
536: bs = escflags & ESC_BOL && !code_blocks;
537: break;
538: case '(':
539: bs = escflags & ESC_HYP && !code_blocks;
540: break;
541: case ')':
1.14 schwarze 542: bs = escflags & ESC_NUM && !code_blocks;
1.1 schwarze 543: break;
544: case '*':
545: case '[':
546: case '_':
547: case '`':
548: bs = !code_blocks;
549: break;
550: case '.':
551: bs = escflags & ESC_NUM && !code_blocks;
552: break;
553: case '<':
554: if (code_blocks == 0) {
555: md_named("lt");
556: c = '\0';
557: }
558: break;
559: case '=':
560: if (escflags & ESC_BOL && !code_blocks) {
561: md_named("equals");
562: c = '\0';
563: }
564: break;
565: case '>':
566: if (code_blocks == 0) {
567: md_named("gt");
568: c = '\0';
569: }
570: break;
571: case '\\':
572: uc = 0;
573: nextfont = NULL;
574: switch (mandoc_escape(&s, &seq, &sz)) {
575: case ESCAPE_UNICODE:
576: uc = mchars_num2uc(seq + 1, sz - 1);
577: break;
578: case ESCAPE_NUMBERED:
579: uc = mchars_num2char(seq, sz);
580: break;
581: case ESCAPE_SPECIAL:
582: uc = mchars_spec2cp(seq, sz);
583: break;
584: case ESCAPE_FONTBOLD:
585: nextfont = "**";
586: break;
587: case ESCAPE_FONTITALIC:
588: nextfont = "*";
589: break;
590: case ESCAPE_FONTBI:
591: nextfont = "***";
592: break;
593: case ESCAPE_FONT:
594: case ESCAPE_FONTROMAN:
595: nextfont = "";
596: break;
597: case ESCAPE_FONTPREV:
598: nextfont = prevfont;
599: break;
600: case ESCAPE_NOSPACE:
601: case ESCAPE_SKIPCHAR:
602: case ESCAPE_OVERSTRIKE:
603: /* XXX not implemented */
604: /* FALLTHROUGH */
605: case ESCAPE_ERROR:
606: default:
607: break;
608: }
609: if (nextfont != NULL && !code_blocks) {
610: if (*currfont != '\0') {
611: outflags &= ~MD_spc;
612: md_rawword(currfont);
613: }
614: prevfont = currfont;
615: currfont = nextfont;
616: if (*currfont != '\0') {
617: outflags &= ~MD_spc;
618: md_rawword(currfont);
619: }
620: }
621: if (uc) {
622: if ((uc < 0x20 && uc != 0x09) ||
623: (uc > 0x7E && uc < 0xA0))
624: uc = 0xFFFD;
625: if (code_blocks) {
626: seq = mchars_uc2str(uc);
627: fputs(seq, stdout);
628: outcount += strlen(seq);
629: } else {
630: printf("&#%d;", uc);
631: outcount++;
632: }
633: escflags &= ~ESC_FON;
634: }
635: c = '\0';
636: break;
637: case ']':
638: bs = escflags & ESC_SQU && !code_blocks;
639: escflags |= ESC_HYP;
640: break;
641: default:
642: break;
643: }
644: if (bs)
645: putchar('\\');
646: md_char(c);
647: }
648: if (*currfont != '\0') {
649: outflags &= ~MD_spc;
650: md_rawword(currfont);
1.8 schwarze 651: } else if (s[-2] == ' ')
652: escflags |= ESC_EOL;
653: else
654: escflags &= ~ESC_EOL;
1.1 schwarze 655: }
656:
657: /*
658: * Print a single HTML named character reference.
659: */
660: static void
661: md_named(const char *s)
662: {
663: printf("&%s;", s);
1.8 schwarze 664: escflags &= ~(ESC_FON | ESC_EOL);
1.1 schwarze 665: outcount++;
666: }
667:
668: /*
669: * Print a single raw character and maintain certain escape flags.
670: */
671: static void
672: md_char(unsigned char c)
673: {
674: if (c != '\0') {
675: putchar(c);
676: if (c == '*')
677: escflags |= ESC_FON;
678: else
679: escflags &= ~ESC_FON;
680: outcount++;
681: }
682: if (c != ']')
683: escflags &= ~ESC_HYP;
684: if (c == ' ' || c == '\t' || c == '>')
685: return;
686: if (isdigit(c) == 0)
687: escflags &= ~ESC_NUM;
688: else if (escflags & ESC_BOL)
689: escflags |= ESC_NUM;
690: escflags &= ~ESC_BOL;
691: }
692:
693: static int
694: md_cond_head(struct roff_node *n)
695: {
696: return n->type == ROFFT_HEAD;
697: }
698:
699: static int
700: md_cond_body(struct roff_node *n)
701: {
702: return n->type == ROFFT_BODY;
703: }
704:
705: static int
706: md_pre_raw(struct roff_node *n)
707: {
708: const char *prefix;
709:
710: if ((prefix = md_acts[n->tok].prefix) != NULL) {
711: md_rawword(prefix);
712: outflags &= ~MD_spc;
1.13 schwarze 713: if (*prefix == '`')
714: code_blocks++;
1.1 schwarze 715: }
716: return 1;
717: }
718:
719: static void
720: md_post_raw(struct roff_node *n)
721: {
722: const char *suffix;
723:
724: if ((suffix = md_acts[n->tok].suffix) != NULL) {
725: outflags &= ~(MD_spc | MD_nl);
726: md_rawword(suffix);
1.13 schwarze 727: if (*suffix == '`')
728: code_blocks--;
1.1 schwarze 729: }
730: }
731:
732: static int
733: md_pre_word(struct roff_node *n)
734: {
735: const char *prefix;
736:
737: if ((prefix = md_acts[n->tok].prefix) != NULL) {
738: md_word(prefix);
739: outflags &= ~MD_spc;
740: }
741: return 1;
742: }
743:
744: static void
745: md_post_word(struct roff_node *n)
746: {
747: const char *suffix;
748:
749: if ((suffix = md_acts[n->tok].suffix) != NULL) {
750: outflags &= ~(MD_spc | MD_nl);
751: md_word(suffix);
752: }
753: }
754:
755: static void
756: md_post_pc(struct roff_node *n)
757: {
758: md_post_raw(n);
759: if (n->parent->tok != MDOC_Rs)
760: return;
761: if (n->next != NULL) {
762: md_word(",");
763: if (n->prev != NULL &&
764: n->prev->tok == n->tok &&
765: n->next->tok == n->tok)
766: md_word("and");
767: } else {
768: md_word(".");
769: outflags |= MD_nl;
770: }
771: }
772:
773: static int
774: md_pre_skip(struct roff_node *n)
775: {
776: return 0;
777: }
778:
779: static void
780: md_pre_syn(struct roff_node *n)
781: {
782: if (n->prev == NULL || ! (n->flags & NODE_SYNPRETTY))
783: return;
784:
785: if (n->prev->tok == n->tok &&
786: n->tok != MDOC_Ft &&
787: n->tok != MDOC_Fo &&
788: n->tok != MDOC_Fn) {
789: outflags |= MD_br;
790: return;
791: }
792:
793: switch (n->prev->tok) {
794: case MDOC_Fd:
795: case MDOC_Fn:
796: case MDOC_Fo:
797: case MDOC_In:
798: case MDOC_Vt:
799: outflags |= MD_sp;
800: break;
801: case MDOC_Ft:
802: if (n->tok != MDOC_Fn && n->tok != MDOC_Fo) {
803: outflags |= MD_sp;
804: break;
805: }
806: /* FALLTHROUGH */
807: default:
808: outflags |= MD_br;
809: break;
810: }
811: }
812:
813: static int
1.5 schwarze 814: md_pre_An(struct roff_node *n)
815: {
816: switch (n->norm->An.auth) {
817: case AUTH_split:
818: outflags &= ~MD_An_nosplit;
819: outflags |= MD_An_split;
820: return 0;
821: case AUTH_nosplit:
822: outflags &= ~MD_An_split;
823: outflags |= MD_An_nosplit;
824: return 0;
825: default:
826: if (outflags & MD_An_split)
827: outflags |= MD_br;
828: else if (n->sec == SEC_AUTHORS &&
829: ! (outflags & MD_An_nosplit))
830: outflags |= MD_An_split;
831: return 1;
832: }
833: }
834:
835: static int
1.1 schwarze 836: md_pre_Ap(struct roff_node *n)
837: {
838: outflags &= ~MD_spc;
839: md_word("'");
840: outflags &= ~MD_spc;
841: return 0;
842: }
843:
844: static int
845: md_pre_Bd(struct roff_node *n)
846: {
847: switch (n->norm->Bd.type) {
848: case DISP_unfilled:
849: case DISP_literal:
850: return md_pre_Dl(n);
851: default:
852: return md_pre_D1(n);
853: }
854: }
855:
856: static int
857: md_pre_Bk(struct roff_node *n)
858: {
859: switch (n->type) {
860: case ROFFT_BLOCK:
861: return 1;
862: case ROFFT_BODY:
863: outflags |= MD_Bk;
864: return 1;
865: default:
866: return 0;
867: }
868: }
869:
870: static void
871: md_post_Bk(struct roff_node *n)
872: {
873: if (n->type == ROFFT_BODY)
874: outflags &= ~MD_Bk;
875: }
876:
877: static int
878: md_pre_Bl(struct roff_node *n)
879: {
880: n->norm->Bl.count = 0;
881: if (n->norm->Bl.type == LIST_column)
882: md_pre_Dl(n);
883: outflags |= MD_sp;
884: return 1;
885: }
886:
887: static void
888: md_post_Bl(struct roff_node *n)
889: {
890: n->norm->Bl.count = 0;
891: if (n->norm->Bl.type == LIST_column)
892: md_post_D1(n);
893: outflags |= MD_sp;
894: }
895:
896: static int
897: md_pre_D1(struct roff_node *n)
898: {
899: /*
900: * Markdown blockquote syntax does not work inside code blocks.
901: * The best we can do is fall back to another nested code block.
902: */
903: if (code_blocks) {
904: md_stack('\t');
905: code_blocks++;
906: } else {
907: md_stack('>');
908: quote_blocks++;
909: }
910: outflags |= MD_sp;
911: return 1;
912: }
913:
914: static void
915: md_post_D1(struct roff_node *n)
916: {
917: md_stack((char)-1);
918: if (code_blocks)
919: code_blocks--;
920: else
921: quote_blocks--;
922: outflags |= MD_sp;
923: }
924:
925: static int
926: md_pre_Dl(struct roff_node *n)
927: {
928: /*
929: * Markdown code block syntax does not work inside blockquotes.
930: * The best we can do is fall back to another nested blockquote.
931: */
932: if (quote_blocks) {
933: md_stack('>');
934: quote_blocks++;
935: } else {
936: md_stack('\t');
937: code_blocks++;
938: }
939: outflags |= MD_sp;
940: return 1;
941: }
942:
943: static int
944: md_pre_En(struct roff_node *n)
945: {
946: if (n->norm->Es == NULL ||
947: n->norm->Es->child == NULL)
948: return 1;
949:
950: md_word(n->norm->Es->child->string);
951: outflags &= ~MD_spc;
952: return 1;
953: }
954:
955: static void
956: md_post_En(struct roff_node *n)
957: {
958: if (n->norm->Es == NULL ||
959: n->norm->Es->child == NULL ||
960: n->norm->Es->child->next == NULL)
961: return;
962:
963: outflags &= ~MD_spc;
964: md_word(n->norm->Es->child->next->string);
965: }
966:
967: static int
968: md_pre_Eo(struct roff_node *n)
969: {
970: if (n->end == ENDBODY_NOT &&
971: n->parent->head->child == NULL &&
972: n->child != NULL &&
973: n->child->end != ENDBODY_NOT)
974: md_preword();
975: else if (n->end != ENDBODY_NOT ? n->child != NULL :
976: n->parent->head->child != NULL && (n->child != NULL ||
977: (n->parent->tail != NULL && n->parent->tail->child != NULL)))
978: outflags &= ~(MD_spc | MD_nl);
979: return 1;
980: }
981:
982: static void
983: md_post_Eo(struct roff_node *n)
984: {
985: if (n->end != ENDBODY_NOT) {
986: outflags |= MD_spc;
987: return;
988: }
989:
1.7 schwarze 990: if (n->child == NULL && n->parent->head->child == NULL)
991: return;
1.1 schwarze 992:
1.7 schwarze 993: if (n->parent->tail != NULL && n->parent->tail->child != NULL)
1.1 schwarze 994: outflags &= ~MD_spc;
1.7 schwarze 995: else
1.1 schwarze 996: outflags |= MD_spc;
997: }
998:
999: static int
1000: md_pre_Fa(struct roff_node *n)
1001: {
1002: int am_Fa;
1003:
1004: am_Fa = n->tok == MDOC_Fa;
1005:
1006: if (am_Fa)
1007: n = n->child;
1008:
1009: while (n != NULL) {
1010: md_rawword("*");
1011: outflags &= ~MD_spc;
1012: md_node(n);
1013: outflags &= ~MD_spc;
1014: md_rawword("*");
1015: if ((n = n->next) != NULL)
1016: md_word(",");
1017: }
1018: return 0;
1019: }
1020:
1021: static void
1022: md_post_Fa(struct roff_node *n)
1023: {
1024: if (n->next != NULL && n->next->tok == MDOC_Fa)
1025: md_word(",");
1026: }
1027:
1028: static int
1029: md_pre_Fd(struct roff_node *n)
1030: {
1031: md_pre_syn(n);
1032: md_pre_raw(n);
1033: return 1;
1034: }
1035:
1036: static void
1037: md_post_Fd(struct roff_node *n)
1038: {
1039: md_post_raw(n);
1040: outflags |= MD_br;
1.6 schwarze 1041: }
1042:
1043: static void
1044: md_post_Fl(struct roff_node *n)
1045: {
1046: md_post_raw(n);
1047: if (n->child == NULL && n->next != NULL &&
1048: n->next->type != ROFFT_TEXT && !(n->next->flags & NODE_LINE))
1049: outflags &= ~MD_spc;
1.1 schwarze 1050: }
1051:
1052: static int
1053: md_pre_Fn(struct roff_node *n)
1054: {
1055: md_pre_syn(n);
1056:
1057: if ((n = n->child) == NULL)
1058: return 0;
1059:
1060: md_rawword("**");
1061: outflags &= ~MD_spc;
1062: md_node(n);
1063: outflags &= ~MD_spc;
1064: md_rawword("**");
1065: outflags &= ~MD_spc;
1066: md_word("(");
1067:
1068: if ((n = n->next) != NULL)
1069: md_pre_Fa(n);
1070: return 0;
1071: }
1072:
1073: static void
1074: md_post_Fn(struct roff_node *n)
1075: {
1076: md_word(")");
1077: if (n->flags & NODE_SYNPRETTY) {
1078: md_word(";");
1079: outflags |= MD_sp;
1080: }
1081: }
1082:
1083: static int
1084: md_pre_Fo(struct roff_node *n)
1085: {
1086: switch (n->type) {
1087: case ROFFT_BLOCK:
1088: md_pre_syn(n);
1089: break;
1090: case ROFFT_HEAD:
1091: if (n->child == NULL)
1092: return 0;
1093: md_pre_raw(n);
1094: break;
1095: case ROFFT_BODY:
1096: outflags &= ~(MD_spc | MD_nl);
1097: md_word("(");
1098: break;
1099: default:
1100: break;
1101: }
1102: return 1;
1103: }
1104:
1105: static void
1106: md_post_Fo(struct roff_node *n)
1107: {
1108: switch (n->type) {
1109: case ROFFT_HEAD:
1110: if (n->child != NULL)
1111: md_post_raw(n);
1112: break;
1113: case ROFFT_BODY:
1114: md_post_Fn(n);
1115: break;
1116: default:
1117: break;
1118: }
1119: }
1120:
1121: static int
1122: md_pre_In(struct roff_node *n)
1123: {
1124: if (n->flags & NODE_SYNPRETTY) {
1125: md_pre_syn(n);
1.4 schwarze 1126: md_rawword("**");
1.1 schwarze 1127: outflags &= ~MD_spc;
1128: md_word("#include <");
1129: } else {
1130: md_word("<");
1131: outflags &= ~MD_spc;
1.4 schwarze 1132: md_rawword("*");
1.1 schwarze 1133: }
1.4 schwarze 1134: outflags &= ~MD_spc;
1.1 schwarze 1135: return 1;
1136: }
1137:
1138: static void
1139: md_post_In(struct roff_node *n)
1140: {
1141: if (n->flags & NODE_SYNPRETTY) {
1142: outflags &= ~MD_spc;
1.4 schwarze 1143: md_rawword(">**");
1.1 schwarze 1144: outflags |= MD_nl;
1145: } else {
1146: outflags &= ~MD_spc;
1.4 schwarze 1147: md_rawword("*>");
1.1 schwarze 1148: }
1149: }
1150:
1151: static int
1152: md_pre_It(struct roff_node *n)
1153: {
1154: struct roff_node *bln;
1155:
1156: switch (n->type) {
1157: case ROFFT_BLOCK:
1158: return 1;
1159:
1160: case ROFFT_HEAD:
1161: bln = n->parent->parent;
1.10 schwarze 1162: if (bln->norm->Bl.comp == 0 &&
1163: bln->norm->Bl.type != LIST_column)
1.1 schwarze 1164: outflags |= MD_sp;
1165: outflags |= MD_nl;
1166:
1167: switch (bln->norm->Bl.type) {
1168: case LIST_item:
1169: outflags |= MD_br;
1170: return 0;
1171: case LIST_inset:
1172: case LIST_diag:
1173: case LIST_ohang:
1174: outflags |= MD_br;
1175: return 1;
1176: case LIST_tag:
1177: case LIST_hang:
1178: outflags |= MD_sp;
1179: return 1;
1180: case LIST_bullet:
1181: md_rawword("*\t");
1182: break;
1183: case LIST_dash:
1184: case LIST_hyphen:
1185: md_rawword("-\t");
1186: break;
1187: case LIST_enum:
1188: md_preword();
1.11 schwarze 1189: if (bln->norm->Bl.count < 99)
1190: bln->norm->Bl.count++;
1191: printf("%d.\t", bln->norm->Bl.count);
1.1 schwarze 1192: escflags &= ~ESC_FON;
1193: break;
1.10 schwarze 1194: case LIST_column:
1195: outflags |= MD_br;
1196: return 0;
1.1 schwarze 1197: default:
1198: return 0;
1199: }
1200: outflags &= ~MD_spc;
1201: outflags |= MD_nonl;
1202: outcount = 0;
1203: md_stack('\t');
1204: if (code_blocks || quote_blocks)
1205: list_blocks++;
1206: return 0;
1207:
1208: case ROFFT_BODY:
1209: bln = n->parent->parent;
1210: switch (bln->norm->Bl.type) {
1211: case LIST_ohang:
1212: outflags |= MD_br;
1213: break;
1214: case LIST_tag:
1215: case LIST_hang:
1216: md_pre_D1(n);
1217: break;
1218: default:
1219: break;
1220: }
1221: return 1;
1222:
1223: default:
1224: return 0;
1225: }
1226: }
1227:
1228: static void
1229: md_post_It(struct roff_node *n)
1230: {
1231: struct roff_node *bln;
1232: int i, nc;
1233:
1234: if (n->type != ROFFT_BODY)
1235: return;
1236:
1237: bln = n->parent->parent;
1238: switch (bln->norm->Bl.type) {
1239: case LIST_bullet:
1240: case LIST_dash:
1241: case LIST_hyphen:
1242: case LIST_enum:
1243: md_stack((char)-1);
1244: if (code_blocks || quote_blocks)
1245: list_blocks--;
1246: break;
1247: case LIST_tag:
1248: case LIST_hang:
1249: md_post_D1(n);
1250: break;
1251:
1252: case LIST_column:
1253: if (n->next == NULL)
1254: break;
1255:
1256: /* Calculate the array index of the current column. */
1257:
1258: i = 0;
1259: while ((n = n->prev) != NULL && n->type != ROFFT_HEAD)
1260: i++;
1261:
1262: /*
1263: * If a width was specified for this column,
1264: * subtract what printed, and
1265: * add the same spacing as in mdoc_term.c.
1266: */
1267:
1268: nc = bln->norm->Bl.ncols;
1269: i = i < nc ? strlen(bln->norm->Bl.cols[i]) - outcount +
1270: (nc < 5 ? 4 : nc == 5 ? 3 : 1) : 1;
1271: if (i < 1)
1272: i = 1;
1273: while (i-- > 0)
1274: putchar(' ');
1275:
1276: outflags &= ~MD_spc;
1277: escflags &= ~ESC_FON;
1278: outcount = 0;
1279: break;
1280:
1281: default:
1282: break;
1283: }
1284: }
1285:
1286: static void
1287: md_post_Lb(struct roff_node *n)
1288: {
1289: if (n->sec == SEC_LIBRARY)
1290: outflags |= MD_br;
1291: }
1292:
1.15 schwarze 1293: static void
1294: md_uri(const char *s)
1295: {
1296: while (*s != '\0') {
1297: if (strchr("%()<>", *s) != NULL) {
1298: printf("%%%2.2hhX", *s);
1299: outcount += 3;
1300: } else {
1301: putchar(*s);
1302: outcount++;
1303: }
1304: s++;
1305: }
1306: }
1307:
1.1 schwarze 1308: static int
1309: md_pre_Lk(struct roff_node *n)
1310: {
1311: const struct roff_node *link, *descr;
1312:
1313: if ((link = n->child) == NULL)
1314: return 0;
1315:
1.16 schwarze 1316: /* Link text. */
1317: descr = link->next;
1318: if (descr == NULL || descr->flags & NODE_DELIMC)
1319: descr = link; /* no text */
1.15 schwarze 1320: md_rawword("[");
1321: outflags &= ~MD_spc;
1322: do {
1323: md_word(descr->string);
1.16 schwarze 1324: descr = descr->next;
1325: } while (descr != NULL && !(descr->flags & NODE_DELIMC));
1.15 schwarze 1326: outflags &= ~MD_spc;
1.16 schwarze 1327:
1328: /* Link target. */
1.15 schwarze 1329: md_rawword("](");
1330: md_uri(link->string);
1331: outflags &= ~MD_spc;
1332: md_rawword(")");
1.16 schwarze 1333:
1334: /* Trailing punctuation. */
1335: while (descr != NULL) {
1336: md_word(descr->string);
1337: descr = descr->next;
1338: }
1.15 schwarze 1339: return 0;
1340: }
1341:
1342: static int
1343: md_pre_Mt(struct roff_node *n)
1344: {
1345: const struct roff_node *nch;
1.1 schwarze 1346:
1.15 schwarze 1347: md_rawword("[");
1348: outflags &= ~MD_spc;
1349: for (nch = n->child; nch != NULL; nch = nch->next)
1350: md_word(nch->string);
1351: outflags &= ~MD_spc;
1352: md_rawword("](mailto:");
1353: for (nch = n->child; nch != NULL; nch = nch->next) {
1354: md_uri(nch->string);
1355: if (nch->next != NULL) {
1356: putchar(' ');
1.3 schwarze 1357: outcount++;
1358: }
1359: }
1.1 schwarze 1360: outflags &= ~MD_spc;
1.15 schwarze 1361: md_rawword(")");
1.1 schwarze 1362: return 0;
1363: }
1364:
1365: static int
1366: md_pre_Nd(struct roff_node *n)
1367: {
1368: outflags &= ~MD_nl;
1369: outflags |= MD_spc;
1370: md_word("-");
1371: return 1;
1372: }
1373:
1374: static int
1375: md_pre_Nm(struct roff_node *n)
1376: {
1377: switch (n->type) {
1378: case ROFFT_BLOCK:
1379: outflags |= MD_Bk;
1380: md_pre_syn(n);
1381: break;
1382: case ROFFT_HEAD:
1383: case ROFFT_ELEM:
1384: md_pre_raw(n);
1385: break;
1386: default:
1387: break;
1388: }
1389: return 1;
1390: }
1391:
1392: static void
1393: md_post_Nm(struct roff_node *n)
1394: {
1395: switch (n->type) {
1396: case ROFFT_BLOCK:
1397: outflags &= ~MD_Bk;
1398: break;
1399: case ROFFT_HEAD:
1400: case ROFFT_ELEM:
1401: md_post_raw(n);
1402: break;
1403: default:
1404: break;
1405: }
1406: }
1407:
1408: static int
1409: md_pre_No(struct roff_node *n)
1410: {
1411: outflags |= MD_spc_force;
1412: return 1;
1413: }
1414:
1415: static int
1416: md_pre_Ns(struct roff_node *n)
1417: {
1418: outflags &= ~MD_spc;
1419: return 0;
1420: }
1421:
1422: static void
1423: md_post_Pf(struct roff_node *n)
1424: {
1425: if (n->next != NULL && (n->next->flags & NODE_LINE) == 0)
1426: outflags &= ~MD_spc;
1427: }
1428:
1429: static int
1430: md_pre_Pp(struct roff_node *n)
1431: {
1432: outflags |= MD_sp;
1433: return 0;
1434: }
1435:
1436: static int
1437: md_pre_Rs(struct roff_node *n)
1438: {
1439: if (n->sec == SEC_SEE_ALSO)
1440: outflags |= MD_sp;
1441: return 1;
1442: }
1443:
1444: static int
1445: md_pre_Sh(struct roff_node *n)
1446: {
1447: switch (n->type) {
1.5 schwarze 1448: case ROFFT_BLOCK:
1449: if (n->sec == SEC_AUTHORS)
1450: outflags &= ~(MD_An_split | MD_An_nosplit);
1451: break;
1.1 schwarze 1452: case ROFFT_HEAD:
1453: outflags |= MD_sp;
1454: md_rawword(n->tok == MDOC_Sh ? "#" : "##");
1455: break;
1456: case ROFFT_BODY:
1457: outflags |= MD_sp;
1458: break;
1459: default:
1460: break;
1461: }
1462: return 1;
1463: }
1464:
1465: static int
1466: md_pre_Sm(struct roff_node *n)
1467: {
1468: if (n->child == NULL)
1469: outflags ^= MD_Sm;
1470: else if (strcmp("on", n->child->string) == 0)
1471: outflags |= MD_Sm;
1472: else
1473: outflags &= ~MD_Sm;
1474:
1475: if (outflags & MD_Sm)
1476: outflags |= MD_spc;
1477:
1478: return 0;
1479: }
1480:
1481: static int
1482: md_pre_Vt(struct roff_node *n)
1483: {
1484: switch (n->type) {
1485: case ROFFT_BLOCK:
1486: md_pre_syn(n);
1487: return 1;
1488: case ROFFT_BODY:
1489: case ROFFT_ELEM:
1490: md_pre_raw(n);
1491: return 1;
1492: default:
1493: return 0;
1494: }
1495: }
1496:
1497: static void
1498: md_post_Vt(struct roff_node *n)
1499: {
1500: switch (n->type) {
1501: case ROFFT_BODY:
1502: case ROFFT_ELEM:
1503: md_post_raw(n);
1504: break;
1505: default:
1506: break;
1507: }
1508: }
1509:
1510: static int
1511: md_pre_Xr(struct roff_node *n)
1512: {
1513: n = n->child;
1514: if (n == NULL)
1515: return 0;
1516: md_node(n);
1517: n = n->next;
1518: if (n == NULL)
1519: return 0;
1520: outflags &= ~MD_spc;
1521: md_word("(");
1522: md_node(n);
1523: md_word(")");
1524: return 0;
1525: }
1526:
1527: static int
1528: md_pre__T(struct roff_node *n)
1529: {
1.2 schwarze 1530: if (n->parent->tok == MDOC_Rs && n->parent->norm->Rs.quote_T)
1.1 schwarze 1531: md_word("\"");
1532: else
1533: md_rawword("*");
1534: outflags &= ~MD_spc;
1535: return 1;
1536: }
1537:
1538: static void
1539: md_post__T(struct roff_node *n)
1540: {
1541: outflags &= ~MD_spc;
1.2 schwarze 1542: if (n->parent->tok == MDOC_Rs && n->parent->norm->Rs.quote_T)
1.1 schwarze 1543: md_word("\"");
1544: else
1545: md_rawword("*");
1546: md_post_pc(n);
1547: }
1548:
1549: static int
1550: md_pre_br(struct roff_node *n)
1551: {
1552: outflags |= MD_br;
1553: return 0;
1554: }
CVSweb