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