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