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