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