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