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