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