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