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