Annotation of mandoc/mdoc_markdown.c, Revision 1.28
1.28 ! schwarze 1: /* $Id: mdoc_markdown.c,v 1.27 2018/10/25 01:32:40 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
264: markdown_mdoc(void *arg, const struct roff_man *mdoc)
265: {
266: outflags = MD_Sm;
267: md_word(mdoc->meta.title);
268: if (mdoc->meta.msec != NULL) {
269: outflags &= ~MD_spc;
270: md_word("(");
271: md_word(mdoc->meta.msec);
272: md_word(")");
273: }
274: md_word("-");
275: md_word(mdoc->meta.vol);
276: if (mdoc->meta.arch != NULL) {
277: md_word("(");
278: md_word(mdoc->meta.arch);
279: md_word(")");
280: }
281: outflags |= MD_sp;
282:
283: md_nodelist(mdoc->first->child);
284:
285: outflags |= MD_sp;
286: md_word(mdoc->meta.os);
287: md_word("-");
288: md_word(mdoc->meta.date);
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);
591: break;
1.25 schwarze 592: case ESCAPE_DEVICE:
593: md_rawword("markdown");
594: continue;
1.1 schwarze 595: case ESCAPE_FONTBOLD:
596: nextfont = "**";
597: break;
598: case ESCAPE_FONTITALIC:
599: nextfont = "*";
600: break;
601: case ESCAPE_FONTBI:
602: nextfont = "***";
603: break;
604: case ESCAPE_FONT:
1.27 schwarze 605: case ESCAPE_FONTCW:
1.1 schwarze 606: case ESCAPE_FONTROMAN:
607: nextfont = "";
608: break;
609: case ESCAPE_FONTPREV:
610: nextfont = prevfont;
611: break;
1.23 schwarze 612: case ESCAPE_BREAK:
613: breakline = 1;
614: break;
1.1 schwarze 615: case ESCAPE_NOSPACE:
616: case ESCAPE_SKIPCHAR:
617: case ESCAPE_OVERSTRIKE:
618: /* XXX not implemented */
619: /* FALLTHROUGH */
620: case ESCAPE_ERROR:
621: default:
622: break;
623: }
624: if (nextfont != NULL && !code_blocks) {
625: if (*currfont != '\0') {
626: outflags &= ~MD_spc;
627: md_rawword(currfont);
628: }
629: prevfont = currfont;
630: currfont = nextfont;
631: if (*currfont != '\0') {
632: outflags &= ~MD_spc;
633: md_rawword(currfont);
634: }
635: }
636: if (uc) {
637: if ((uc < 0x20 && uc != 0x09) ||
638: (uc > 0x7E && uc < 0xA0))
639: uc = 0xFFFD;
640: if (code_blocks) {
641: seq = mchars_uc2str(uc);
642: fputs(seq, stdout);
643: outcount += strlen(seq);
644: } else {
645: printf("&#%d;", uc);
646: outcount++;
647: }
648: escflags &= ~ESC_FON;
649: }
650: c = '\0';
651: break;
652: case ']':
653: bs = escflags & ESC_SQU && !code_blocks;
654: escflags |= ESC_HYP;
655: break;
656: default:
657: break;
658: }
659: if (bs)
660: putchar('\\');
661: md_char(c);
1.23 schwarze 662: if (breakline &&
663: (*s == '\0' || *s == ' ' || *s == ASCII_NBRSP)) {
664: printf(" \n");
665: breakline = 0;
666: while (*s == ' ' || *s == ASCII_NBRSP)
667: s++;
668: }
1.1 schwarze 669: }
670: if (*currfont != '\0') {
671: outflags &= ~MD_spc;
672: md_rawword(currfont);
1.8 schwarze 673: } else if (s[-2] == ' ')
674: escflags |= ESC_EOL;
675: else
676: escflags &= ~ESC_EOL;
1.1 schwarze 677: }
678:
679: /*
680: * Print a single HTML named character reference.
681: */
682: static void
683: md_named(const char *s)
684: {
685: printf("&%s;", s);
1.8 schwarze 686: escflags &= ~(ESC_FON | ESC_EOL);
1.1 schwarze 687: outcount++;
688: }
689:
690: /*
691: * Print a single raw character and maintain certain escape flags.
692: */
693: static void
694: md_char(unsigned char c)
695: {
696: if (c != '\0') {
697: putchar(c);
698: if (c == '*')
699: escflags |= ESC_FON;
700: else
701: escflags &= ~ESC_FON;
702: outcount++;
703: }
704: if (c != ']')
705: escflags &= ~ESC_HYP;
706: if (c == ' ' || c == '\t' || c == '>')
707: return;
708: if (isdigit(c) == 0)
709: escflags &= ~ESC_NUM;
710: else if (escflags & ESC_BOL)
711: escflags |= ESC_NUM;
712: escflags &= ~ESC_BOL;
713: }
714:
715: static int
716: md_cond_head(struct roff_node *n)
717: {
718: return n->type == ROFFT_HEAD;
719: }
720:
721: static int
722: md_cond_body(struct roff_node *n)
723: {
724: return n->type == ROFFT_BODY;
1.28 ! schwarze 725: }
! 726:
! 727: static int
! 728: md_pre_abort(struct roff_node *n)
! 729: {
! 730: abort();
1.1 schwarze 731: }
732:
733: static int
734: md_pre_raw(struct roff_node *n)
735: {
736: const char *prefix;
737:
1.26 schwarze 738: if ((prefix = md_act(n->tok)->prefix) != NULL) {
1.1 schwarze 739: md_rawword(prefix);
740: outflags &= ~MD_spc;
1.13 schwarze 741: if (*prefix == '`')
742: code_blocks++;
1.1 schwarze 743: }
744: return 1;
745: }
746:
747: static void
748: md_post_raw(struct roff_node *n)
749: {
750: const char *suffix;
751:
1.26 schwarze 752: if ((suffix = md_act(n->tok)->suffix) != NULL) {
1.1 schwarze 753: outflags &= ~(MD_spc | MD_nl);
754: md_rawword(suffix);
1.13 schwarze 755: if (*suffix == '`')
756: code_blocks--;
1.1 schwarze 757: }
758: }
759:
760: static int
761: md_pre_word(struct roff_node *n)
762: {
763: const char *prefix;
764:
1.26 schwarze 765: if ((prefix = md_act(n->tok)->prefix) != NULL) {
1.1 schwarze 766: md_word(prefix);
767: outflags &= ~MD_spc;
768: }
769: return 1;
770: }
771:
772: static void
773: md_post_word(struct roff_node *n)
774: {
775: const char *suffix;
776:
1.26 schwarze 777: if ((suffix = md_act(n->tok)->suffix) != NULL) {
1.1 schwarze 778: outflags &= ~(MD_spc | MD_nl);
779: md_word(suffix);
780: }
781: }
782:
783: static void
784: md_post_pc(struct roff_node *n)
785: {
786: md_post_raw(n);
787: if (n->parent->tok != MDOC_Rs)
788: return;
789: if (n->next != NULL) {
790: md_word(",");
791: if (n->prev != NULL &&
792: n->prev->tok == n->tok &&
793: n->next->tok == n->tok)
794: md_word("and");
795: } else {
796: md_word(".");
797: outflags |= MD_nl;
798: }
799: }
800:
801: static int
802: md_pre_skip(struct roff_node *n)
803: {
804: return 0;
805: }
806:
807: static void
808: md_pre_syn(struct roff_node *n)
809: {
810: if (n->prev == NULL || ! (n->flags & NODE_SYNPRETTY))
811: return;
812:
813: if (n->prev->tok == n->tok &&
814: n->tok != MDOC_Ft &&
815: n->tok != MDOC_Fo &&
816: n->tok != MDOC_Fn) {
817: outflags |= MD_br;
818: return;
819: }
820:
821: switch (n->prev->tok) {
822: case MDOC_Fd:
823: case MDOC_Fn:
824: case MDOC_Fo:
825: case MDOC_In:
826: case MDOC_Vt:
827: outflags |= MD_sp;
828: break;
829: case MDOC_Ft:
830: if (n->tok != MDOC_Fn && n->tok != MDOC_Fo) {
831: outflags |= MD_sp;
832: break;
833: }
834: /* FALLTHROUGH */
835: default:
836: outflags |= MD_br;
837: break;
838: }
839: }
840:
841: static int
1.5 schwarze 842: md_pre_An(struct roff_node *n)
843: {
844: switch (n->norm->An.auth) {
845: case AUTH_split:
846: outflags &= ~MD_An_nosplit;
847: outflags |= MD_An_split;
848: return 0;
849: case AUTH_nosplit:
850: outflags &= ~MD_An_split;
851: outflags |= MD_An_nosplit;
852: return 0;
853: default:
854: if (outflags & MD_An_split)
855: outflags |= MD_br;
856: else if (n->sec == SEC_AUTHORS &&
857: ! (outflags & MD_An_nosplit))
858: outflags |= MD_An_split;
859: return 1;
860: }
861: }
862:
863: static int
1.1 schwarze 864: md_pre_Ap(struct roff_node *n)
865: {
866: outflags &= ~MD_spc;
867: md_word("'");
868: outflags &= ~MD_spc;
869: return 0;
870: }
871:
872: static int
873: md_pre_Bd(struct roff_node *n)
874: {
875: switch (n->norm->Bd.type) {
876: case DISP_unfilled:
877: case DISP_literal:
878: return md_pre_Dl(n);
879: default:
880: return md_pre_D1(n);
881: }
882: }
883:
884: static int
885: md_pre_Bk(struct roff_node *n)
886: {
887: switch (n->type) {
888: case ROFFT_BLOCK:
889: return 1;
890: case ROFFT_BODY:
891: outflags |= MD_Bk;
892: return 1;
893: default:
894: return 0;
895: }
896: }
897:
898: static void
899: md_post_Bk(struct roff_node *n)
900: {
901: if (n->type == ROFFT_BODY)
902: outflags &= ~MD_Bk;
903: }
904:
905: static int
906: md_pre_Bl(struct roff_node *n)
907: {
908: n->norm->Bl.count = 0;
909: if (n->norm->Bl.type == LIST_column)
910: md_pre_Dl(n);
911: outflags |= MD_sp;
912: return 1;
913: }
914:
915: static void
916: md_post_Bl(struct roff_node *n)
917: {
918: n->norm->Bl.count = 0;
919: if (n->norm->Bl.type == LIST_column)
920: md_post_D1(n);
921: outflags |= MD_sp;
922: }
923:
924: static int
925: md_pre_D1(struct roff_node *n)
926: {
927: /*
928: * Markdown blockquote syntax does not work inside code blocks.
929: * The best we can do is fall back to another nested code block.
930: */
931: if (code_blocks) {
932: md_stack('\t');
933: code_blocks++;
934: } else {
935: md_stack('>');
936: quote_blocks++;
937: }
938: outflags |= MD_sp;
939: return 1;
940: }
941:
942: static void
943: md_post_D1(struct roff_node *n)
944: {
945: md_stack((char)-1);
946: if (code_blocks)
947: code_blocks--;
948: else
949: quote_blocks--;
950: outflags |= MD_sp;
951: }
952:
953: static int
954: md_pre_Dl(struct roff_node *n)
955: {
956: /*
957: * Markdown code block syntax does not work inside blockquotes.
958: * The best we can do is fall back to another nested blockquote.
959: */
960: if (quote_blocks) {
961: md_stack('>');
962: quote_blocks++;
963: } else {
964: md_stack('\t');
965: code_blocks++;
966: }
967: outflags |= MD_sp;
968: return 1;
969: }
970:
971: static int
972: md_pre_En(struct roff_node *n)
973: {
974: if (n->norm->Es == NULL ||
975: n->norm->Es->child == NULL)
976: return 1;
977:
978: md_word(n->norm->Es->child->string);
979: outflags &= ~MD_spc;
980: return 1;
981: }
982:
983: static void
984: md_post_En(struct roff_node *n)
985: {
986: if (n->norm->Es == NULL ||
987: n->norm->Es->child == NULL ||
988: n->norm->Es->child->next == NULL)
989: return;
990:
991: outflags &= ~MD_spc;
992: md_word(n->norm->Es->child->next->string);
993: }
994:
995: static int
996: md_pre_Eo(struct roff_node *n)
997: {
998: if (n->end == ENDBODY_NOT &&
999: n->parent->head->child == NULL &&
1000: n->child != NULL &&
1001: n->child->end != ENDBODY_NOT)
1002: md_preword();
1003: else if (n->end != ENDBODY_NOT ? n->child != NULL :
1004: n->parent->head->child != NULL && (n->child != NULL ||
1005: (n->parent->tail != NULL && n->parent->tail->child != NULL)))
1006: outflags &= ~(MD_spc | MD_nl);
1007: return 1;
1008: }
1009:
1010: static void
1011: md_post_Eo(struct roff_node *n)
1012: {
1013: if (n->end != ENDBODY_NOT) {
1014: outflags |= MD_spc;
1015: return;
1016: }
1017:
1.7 schwarze 1018: if (n->child == NULL && n->parent->head->child == NULL)
1019: return;
1.1 schwarze 1020:
1.7 schwarze 1021: if (n->parent->tail != NULL && n->parent->tail->child != NULL)
1.1 schwarze 1022: outflags &= ~MD_spc;
1.7 schwarze 1023: else
1.1 schwarze 1024: outflags |= MD_spc;
1025: }
1026:
1027: static int
1028: md_pre_Fa(struct roff_node *n)
1029: {
1030: int am_Fa;
1031:
1032: am_Fa = n->tok == MDOC_Fa;
1033:
1034: if (am_Fa)
1035: n = n->child;
1036:
1037: while (n != NULL) {
1038: md_rawword("*");
1039: outflags &= ~MD_spc;
1040: md_node(n);
1041: outflags &= ~MD_spc;
1042: md_rawword("*");
1043: if ((n = n->next) != NULL)
1044: md_word(",");
1045: }
1046: return 0;
1047: }
1048:
1049: static void
1050: md_post_Fa(struct roff_node *n)
1051: {
1052: if (n->next != NULL && n->next->tok == MDOC_Fa)
1053: md_word(",");
1054: }
1055:
1056: static int
1057: md_pre_Fd(struct roff_node *n)
1058: {
1059: md_pre_syn(n);
1060: md_pre_raw(n);
1061: return 1;
1062: }
1063:
1064: static void
1065: md_post_Fd(struct roff_node *n)
1066: {
1067: md_post_raw(n);
1068: outflags |= MD_br;
1.6 schwarze 1069: }
1070:
1071: static void
1072: md_post_Fl(struct roff_node *n)
1073: {
1074: md_post_raw(n);
1075: if (n->child == NULL && n->next != NULL &&
1076: n->next->type != ROFFT_TEXT && !(n->next->flags & NODE_LINE))
1077: outflags &= ~MD_spc;
1.1 schwarze 1078: }
1079:
1080: static int
1081: md_pre_Fn(struct roff_node *n)
1082: {
1083: md_pre_syn(n);
1084:
1085: if ((n = n->child) == NULL)
1086: return 0;
1087:
1088: md_rawword("**");
1089: outflags &= ~MD_spc;
1090: md_node(n);
1091: outflags &= ~MD_spc;
1092: md_rawword("**");
1093: outflags &= ~MD_spc;
1094: md_word("(");
1095:
1096: if ((n = n->next) != NULL)
1097: md_pre_Fa(n);
1098: return 0;
1099: }
1100:
1101: static void
1102: md_post_Fn(struct roff_node *n)
1103: {
1104: md_word(")");
1105: if (n->flags & NODE_SYNPRETTY) {
1106: md_word(";");
1107: outflags |= MD_sp;
1108: }
1109: }
1110:
1111: static int
1112: md_pre_Fo(struct roff_node *n)
1113: {
1114: switch (n->type) {
1115: case ROFFT_BLOCK:
1116: md_pre_syn(n);
1117: break;
1118: case ROFFT_HEAD:
1119: if (n->child == NULL)
1120: return 0;
1121: md_pre_raw(n);
1122: break;
1123: case ROFFT_BODY:
1124: outflags &= ~(MD_spc | MD_nl);
1125: md_word("(");
1126: break;
1127: default:
1128: break;
1129: }
1130: return 1;
1131: }
1132:
1133: static void
1134: md_post_Fo(struct roff_node *n)
1135: {
1136: switch (n->type) {
1137: case ROFFT_HEAD:
1138: if (n->child != NULL)
1139: md_post_raw(n);
1140: break;
1141: case ROFFT_BODY:
1142: md_post_Fn(n);
1143: break;
1144: default:
1145: break;
1146: }
1147: }
1148:
1149: static int
1150: md_pre_In(struct roff_node *n)
1151: {
1152: if (n->flags & NODE_SYNPRETTY) {
1153: md_pre_syn(n);
1.4 schwarze 1154: md_rawword("**");
1.1 schwarze 1155: outflags &= ~MD_spc;
1156: md_word("#include <");
1157: } else {
1158: md_word("<");
1159: outflags &= ~MD_spc;
1.4 schwarze 1160: md_rawword("*");
1.1 schwarze 1161: }
1.4 schwarze 1162: outflags &= ~MD_spc;
1.1 schwarze 1163: return 1;
1164: }
1165:
1166: static void
1167: md_post_In(struct roff_node *n)
1168: {
1169: if (n->flags & NODE_SYNPRETTY) {
1170: outflags &= ~MD_spc;
1.4 schwarze 1171: md_rawword(">**");
1.1 schwarze 1172: outflags |= MD_nl;
1173: } else {
1174: outflags &= ~MD_spc;
1.4 schwarze 1175: md_rawword("*>");
1.1 schwarze 1176: }
1177: }
1178:
1179: static int
1180: md_pre_It(struct roff_node *n)
1181: {
1182: struct roff_node *bln;
1183:
1184: switch (n->type) {
1185: case ROFFT_BLOCK:
1186: return 1;
1187:
1188: case ROFFT_HEAD:
1189: bln = n->parent->parent;
1.10 schwarze 1190: if (bln->norm->Bl.comp == 0 &&
1191: bln->norm->Bl.type != LIST_column)
1.1 schwarze 1192: outflags |= MD_sp;
1193: outflags |= MD_nl;
1194:
1195: switch (bln->norm->Bl.type) {
1196: case LIST_item:
1197: outflags |= MD_br;
1198: return 0;
1199: case LIST_inset:
1200: case LIST_diag:
1201: case LIST_ohang:
1202: outflags |= MD_br;
1203: return 1;
1204: case LIST_tag:
1205: case LIST_hang:
1206: outflags |= MD_sp;
1207: return 1;
1208: case LIST_bullet:
1209: md_rawword("*\t");
1210: break;
1211: case LIST_dash:
1212: case LIST_hyphen:
1213: md_rawword("-\t");
1214: break;
1215: case LIST_enum:
1216: md_preword();
1.11 schwarze 1217: if (bln->norm->Bl.count < 99)
1218: bln->norm->Bl.count++;
1219: printf("%d.\t", bln->norm->Bl.count);
1.1 schwarze 1220: escflags &= ~ESC_FON;
1221: break;
1.10 schwarze 1222: case LIST_column:
1223: outflags |= MD_br;
1224: return 0;
1.1 schwarze 1225: default:
1226: return 0;
1227: }
1228: outflags &= ~MD_spc;
1229: outflags |= MD_nonl;
1230: outcount = 0;
1231: md_stack('\t');
1232: if (code_blocks || quote_blocks)
1233: list_blocks++;
1234: return 0;
1235:
1236: case ROFFT_BODY:
1237: bln = n->parent->parent;
1238: switch (bln->norm->Bl.type) {
1239: case LIST_ohang:
1240: outflags |= MD_br;
1241: break;
1242: case LIST_tag:
1243: case LIST_hang:
1244: md_pre_D1(n);
1245: break;
1246: default:
1247: break;
1248: }
1249: return 1;
1250:
1251: default:
1252: return 0;
1253: }
1254: }
1255:
1256: static void
1257: md_post_It(struct roff_node *n)
1258: {
1259: struct roff_node *bln;
1260: int i, nc;
1261:
1262: if (n->type != ROFFT_BODY)
1263: return;
1264:
1265: bln = n->parent->parent;
1266: switch (bln->norm->Bl.type) {
1267: case LIST_bullet:
1268: case LIST_dash:
1269: case LIST_hyphen:
1270: case LIST_enum:
1271: md_stack((char)-1);
1272: if (code_blocks || quote_blocks)
1273: list_blocks--;
1274: break;
1275: case LIST_tag:
1276: case LIST_hang:
1277: md_post_D1(n);
1278: break;
1279:
1280: case LIST_column:
1281: if (n->next == NULL)
1282: break;
1283:
1284: /* Calculate the array index of the current column. */
1285:
1286: i = 0;
1287: while ((n = n->prev) != NULL && n->type != ROFFT_HEAD)
1288: i++;
1289:
1290: /*
1291: * If a width was specified for this column,
1292: * subtract what printed, and
1293: * add the same spacing as in mdoc_term.c.
1294: */
1295:
1296: nc = bln->norm->Bl.ncols;
1297: i = i < nc ? strlen(bln->norm->Bl.cols[i]) - outcount +
1298: (nc < 5 ? 4 : nc == 5 ? 3 : 1) : 1;
1299: if (i < 1)
1300: i = 1;
1301: while (i-- > 0)
1302: putchar(' ');
1303:
1304: outflags &= ~MD_spc;
1305: escflags &= ~ESC_FON;
1306: outcount = 0;
1307: break;
1308:
1309: default:
1310: break;
1311: }
1312: }
1313:
1314: static void
1315: md_post_Lb(struct roff_node *n)
1316: {
1317: if (n->sec == SEC_LIBRARY)
1318: outflags |= MD_br;
1319: }
1320:
1.15 schwarze 1321: static void
1322: md_uri(const char *s)
1323: {
1324: while (*s != '\0') {
1325: if (strchr("%()<>", *s) != NULL) {
1326: printf("%%%2.2hhX", *s);
1327: outcount += 3;
1328: } else {
1329: putchar(*s);
1330: outcount++;
1331: }
1332: s++;
1333: }
1334: }
1335:
1.1 schwarze 1336: static int
1337: md_pre_Lk(struct roff_node *n)
1338: {
1.22 schwarze 1339: const struct roff_node *link, *descr, *punct;
1.1 schwarze 1340:
1341: if ((link = n->child) == NULL)
1342: return 0;
1343:
1.22 schwarze 1344: /* Find beginning of trailing punctuation. */
1345: punct = n->last;
1346: while (punct != link && punct->flags & NODE_DELIMC)
1347: punct = punct->prev;
1348: punct = punct->next;
1349:
1.16 schwarze 1350: /* Link text. */
1351: descr = link->next;
1.22 schwarze 1352: if (descr == punct)
1.16 schwarze 1353: descr = link; /* no text */
1.15 schwarze 1354: md_rawword("[");
1355: outflags &= ~MD_spc;
1356: do {
1357: md_word(descr->string);
1.16 schwarze 1358: descr = descr->next;
1.22 schwarze 1359: } while (descr != punct);
1.15 schwarze 1360: outflags &= ~MD_spc;
1.16 schwarze 1361:
1362: /* Link target. */
1.15 schwarze 1363: md_rawword("](");
1364: md_uri(link->string);
1365: outflags &= ~MD_spc;
1366: md_rawword(")");
1.16 schwarze 1367:
1368: /* Trailing punctuation. */
1.22 schwarze 1369: while (punct != NULL) {
1370: md_word(punct->string);
1371: punct = punct->next;
1.16 schwarze 1372: }
1.15 schwarze 1373: return 0;
1374: }
1375:
1376: static int
1377: md_pre_Mt(struct roff_node *n)
1378: {
1379: const struct roff_node *nch;
1.1 schwarze 1380:
1.15 schwarze 1381: md_rawword("[");
1382: outflags &= ~MD_spc;
1383: for (nch = n->child; nch != NULL; nch = nch->next)
1384: md_word(nch->string);
1385: outflags &= ~MD_spc;
1386: md_rawword("](mailto:");
1387: for (nch = n->child; nch != NULL; nch = nch->next) {
1388: md_uri(nch->string);
1389: if (nch->next != NULL) {
1390: putchar(' ');
1.3 schwarze 1391: outcount++;
1392: }
1393: }
1.1 schwarze 1394: outflags &= ~MD_spc;
1.15 schwarze 1395: md_rawword(")");
1.1 schwarze 1396: return 0;
1397: }
1398:
1399: static int
1400: md_pre_Nd(struct roff_node *n)
1401: {
1402: outflags &= ~MD_nl;
1403: outflags |= MD_spc;
1404: md_word("-");
1405: return 1;
1406: }
1407:
1408: static int
1409: md_pre_Nm(struct roff_node *n)
1410: {
1411: switch (n->type) {
1412: case ROFFT_BLOCK:
1413: outflags |= MD_Bk;
1414: md_pre_syn(n);
1415: break;
1416: case ROFFT_HEAD:
1417: case ROFFT_ELEM:
1418: md_pre_raw(n);
1419: break;
1420: default:
1421: break;
1422: }
1423: return 1;
1424: }
1425:
1426: static void
1427: md_post_Nm(struct roff_node *n)
1428: {
1429: switch (n->type) {
1430: case ROFFT_BLOCK:
1431: outflags &= ~MD_Bk;
1432: break;
1433: case ROFFT_HEAD:
1434: case ROFFT_ELEM:
1435: md_post_raw(n);
1436: break;
1437: default:
1438: break;
1439: }
1440: }
1441:
1442: static int
1443: md_pre_No(struct roff_node *n)
1444: {
1445: outflags |= MD_spc_force;
1446: return 1;
1447: }
1448:
1449: static int
1450: md_pre_Ns(struct roff_node *n)
1451: {
1452: outflags &= ~MD_spc;
1453: return 0;
1454: }
1455:
1456: static void
1457: md_post_Pf(struct roff_node *n)
1458: {
1459: if (n->next != NULL && (n->next->flags & NODE_LINE) == 0)
1460: outflags &= ~MD_spc;
1461: }
1462:
1463: static int
1464: md_pre_Pp(struct roff_node *n)
1465: {
1466: outflags |= MD_sp;
1467: return 0;
1468: }
1469:
1470: static int
1471: md_pre_Rs(struct roff_node *n)
1472: {
1473: if (n->sec == SEC_SEE_ALSO)
1474: outflags |= MD_sp;
1475: return 1;
1476: }
1477:
1478: static int
1479: md_pre_Sh(struct roff_node *n)
1480: {
1481: switch (n->type) {
1.5 schwarze 1482: case ROFFT_BLOCK:
1483: if (n->sec == SEC_AUTHORS)
1484: outflags &= ~(MD_An_split | MD_An_nosplit);
1485: break;
1.1 schwarze 1486: case ROFFT_HEAD:
1487: outflags |= MD_sp;
1488: md_rawword(n->tok == MDOC_Sh ? "#" : "##");
1489: break;
1490: case ROFFT_BODY:
1491: outflags |= MD_sp;
1492: break;
1493: default:
1494: break;
1495: }
1496: return 1;
1497: }
1498:
1499: static int
1500: md_pre_Sm(struct roff_node *n)
1501: {
1502: if (n->child == NULL)
1503: outflags ^= MD_Sm;
1504: else if (strcmp("on", n->child->string) == 0)
1505: outflags |= MD_Sm;
1506: else
1507: outflags &= ~MD_Sm;
1508:
1509: if (outflags & MD_Sm)
1510: outflags |= MD_spc;
1511:
1512: return 0;
1513: }
1514:
1515: static int
1516: md_pre_Vt(struct roff_node *n)
1517: {
1518: switch (n->type) {
1519: case ROFFT_BLOCK:
1520: md_pre_syn(n);
1521: return 1;
1522: case ROFFT_BODY:
1523: case ROFFT_ELEM:
1524: md_pre_raw(n);
1525: return 1;
1526: default:
1527: return 0;
1528: }
1529: }
1530:
1531: static void
1532: md_post_Vt(struct roff_node *n)
1533: {
1534: switch (n->type) {
1535: case ROFFT_BODY:
1536: case ROFFT_ELEM:
1537: md_post_raw(n);
1538: break;
1539: default:
1540: break;
1541: }
1542: }
1543:
1544: static int
1545: md_pre_Xr(struct roff_node *n)
1546: {
1547: n = n->child;
1548: if (n == NULL)
1549: return 0;
1550: md_node(n);
1551: n = n->next;
1552: if (n == NULL)
1553: return 0;
1554: outflags &= ~MD_spc;
1555: md_word("(");
1556: md_node(n);
1557: md_word(")");
1558: return 0;
1559: }
1560:
1561: static int
1562: md_pre__T(struct roff_node *n)
1563: {
1.2 schwarze 1564: if (n->parent->tok == MDOC_Rs && n->parent->norm->Rs.quote_T)
1.1 schwarze 1565: md_word("\"");
1566: else
1567: md_rawword("*");
1568: outflags &= ~MD_spc;
1569: return 1;
1570: }
1571:
1572: static void
1573: md_post__T(struct roff_node *n)
1574: {
1575: outflags &= ~MD_spc;
1.2 schwarze 1576: if (n->parent->tok == MDOC_Rs && n->parent->norm->Rs.quote_T)
1.1 schwarze 1577: md_word("\"");
1578: else
1579: md_rawword("*");
1580: md_post_pc(n);
1581: }
1582:
1583: static int
1584: md_pre_br(struct roff_node *n)
1585: {
1586: outflags |= MD_br;
1587: return 0;
1588: }
CVSweb