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