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