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