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