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