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