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