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