Annotation of src/usr.bin/mandoc/mdoc_markdown.c, Revision 1.4
1.4 ! schwarze 1: /* $OpenBSD: mdoc_markdown.c,v 1.3 2017/03/06 14:57:44 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 */
1.4 ! schwarze 132: { NULL, md_pre_In, md_post_In, NULL, NULL }, /* In */
1.1 schwarze 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 */
1.3 schwarze 223: { NULL, md_pre_Lk, md_post_pc, NULL, NULL }, /* %U */
1.1 schwarze 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);
1.4 ! schwarze 1070: md_rawword("**");
1.1 schwarze 1071: outflags &= ~MD_spc;
1072: md_word("#include <");
1073: } else {
1074: md_word("<");
1075: outflags &= ~MD_spc;
1.4 ! schwarze 1076: md_rawword("*");
1.1 schwarze 1077: }
1.4 ! schwarze 1078: outflags &= ~MD_spc;
1.1 schwarze 1079: return 1;
1080: }
1081:
1082: static void
1083: md_post_In(struct roff_node *n)
1084: {
1085: if (n->flags & NODE_SYNPRETTY) {
1086: outflags &= ~MD_spc;
1.4 ! schwarze 1087: md_rawword(">**");
1.1 schwarze 1088: outflags |= MD_nl;
1089: } else {
1090: outflags &= ~MD_spc;
1.4 ! schwarze 1091: md_rawword("*>");
1.1 schwarze 1092: }
1093: }
1094:
1095: static int
1096: md_pre_It(struct roff_node *n)
1097: {
1098: struct roff_node *bln;
1099:
1100: switch (n->type) {
1101: case ROFFT_BLOCK:
1102: return 1;
1103:
1104: case ROFFT_HEAD:
1105: bln = n->parent->parent;
1106: if (bln->norm->Bl.comp == 0)
1107: outflags |= MD_sp;
1108: outflags |= MD_nl;
1109:
1110: switch (bln->norm->Bl.type) {
1111: case LIST_item:
1112: outflags |= MD_br;
1113: return 0;
1114: case LIST_inset:
1115: case LIST_diag:
1116: case LIST_ohang:
1117: outflags |= MD_br;
1118: return 1;
1119: case LIST_tag:
1120: case LIST_hang:
1121: outflags |= MD_sp;
1122: return 1;
1123: case LIST_bullet:
1124: md_rawword("*\t");
1125: break;
1126: case LIST_dash:
1127: case LIST_hyphen:
1128: md_rawword("-\t");
1129: break;
1130: case LIST_enum:
1131: md_preword();
1132: printf("%d.\t", ++bln->norm->Bl.count);
1133: escflags &= ~ESC_FON;
1134: break;
1135: default:
1136: return 0;
1137: }
1138: outflags &= ~MD_spc;
1139: outflags |= MD_nonl;
1140: outcount = 0;
1141: md_stack('\t');
1142: if (code_blocks || quote_blocks)
1143: list_blocks++;
1144: return 0;
1145:
1146: case ROFFT_BODY:
1147: bln = n->parent->parent;
1148: switch (bln->norm->Bl.type) {
1149: case LIST_ohang:
1150: outflags |= MD_br;
1151: break;
1152: case LIST_tag:
1153: case LIST_hang:
1154: md_pre_D1(n);
1155: break;
1156: default:
1157: break;
1158: }
1159: return 1;
1160:
1161: default:
1162: return 0;
1163: }
1164: }
1165:
1166: static void
1167: md_post_It(struct roff_node *n)
1168: {
1169: struct roff_node *bln;
1170: int i, nc;
1171:
1172: if (n->type != ROFFT_BODY)
1173: return;
1174:
1175: bln = n->parent->parent;
1176: switch (bln->norm->Bl.type) {
1177: case LIST_bullet:
1178: case LIST_dash:
1179: case LIST_hyphen:
1180: case LIST_enum:
1181: md_stack((char)-1);
1182: if (code_blocks || quote_blocks)
1183: list_blocks--;
1184: break;
1185: case LIST_tag:
1186: case LIST_hang:
1187: md_post_D1(n);
1188: break;
1189:
1190: case LIST_column:
1191: if (n->next == NULL)
1192: break;
1193:
1194: /* Calculate the array index of the current column. */
1195:
1196: i = 0;
1197: while ((n = n->prev) != NULL && n->type != ROFFT_HEAD)
1198: i++;
1199:
1200: /*
1201: * If a width was specified for this column,
1202: * subtract what printed, and
1203: * add the same spacing as in mdoc_term.c.
1204: */
1205:
1206: nc = bln->norm->Bl.ncols;
1207: i = i < nc ? strlen(bln->norm->Bl.cols[i]) - outcount +
1208: (nc < 5 ? 4 : nc == 5 ? 3 : 1) : 1;
1209: if (i < 1)
1210: i = 1;
1211: while (i-- > 0)
1212: putchar(' ');
1213:
1214: outflags &= ~MD_spc;
1215: escflags &= ~ESC_FON;
1216: outcount = 0;
1217: break;
1218:
1219: default:
1220: break;
1221: }
1222: }
1223:
1224: static void
1225: md_post_Lb(struct roff_node *n)
1226: {
1227: if (n->sec == SEC_LIBRARY)
1228: outflags |= MD_br;
1229: }
1230:
1231: static int
1232: md_pre_Lk(struct roff_node *n)
1233: {
1234: const struct roff_node *link, *descr;
1.3 schwarze 1235: const unsigned char *s;
1.1 schwarze 1236:
1237: if ((link = n->child) == NULL)
1238: return 0;
1239:
1240: if ((descr = link->next) != NULL) {
1241: md_rawword("[");
1242: outflags &= ~MD_spc;
1243: while (descr != NULL) {
1244: md_word(descr->string);
1245: descr = descr->next;
1246: }
1247: outflags &= ~MD_spc;
1248: md_rawword("](");
1249: } else
1250: md_rawword("<");
1251:
1.3 schwarze 1252: for (s = link->string; *s != '\0'; s++) {
1253: if (strchr("%)<>", *s) != NULL) {
1254: printf("%%%2.2hhX", *s);
1255: outcount += 3;
1256: } else {
1257: putchar(*s);
1258: outcount++;
1259: }
1260: }
1261:
1.1 schwarze 1262: outflags &= ~MD_spc;
1263: md_rawword(link->next == NULL ? ">" : ")");
1264: return 0;
1265: }
1266:
1267: static int
1268: md_pre_Nd(struct roff_node *n)
1269: {
1270: outflags &= ~MD_nl;
1271: outflags |= MD_spc;
1272: md_word("-");
1273: return 1;
1274: }
1275:
1276: static int
1277: md_pre_Nm(struct roff_node *n)
1278: {
1279: switch (n->type) {
1280: case ROFFT_BLOCK:
1281: outflags |= MD_Bk;
1282: md_pre_syn(n);
1283: break;
1284: case ROFFT_HEAD:
1285: case ROFFT_ELEM:
1286: md_pre_raw(n);
1287: break;
1288: default:
1289: break;
1290: }
1291: return 1;
1292: }
1293:
1294: static void
1295: md_post_Nm(struct roff_node *n)
1296: {
1297: switch (n->type) {
1298: case ROFFT_BLOCK:
1299: outflags &= ~MD_Bk;
1300: break;
1301: case ROFFT_HEAD:
1302: case ROFFT_ELEM:
1303: md_post_raw(n);
1304: break;
1305: default:
1306: break;
1307: }
1308: }
1309:
1310: static int
1311: md_pre_No(struct roff_node *n)
1312: {
1313: outflags |= MD_spc_force;
1314: return 1;
1315: }
1316:
1317: static int
1318: md_pre_Ns(struct roff_node *n)
1319: {
1320: outflags &= ~MD_spc;
1321: return 0;
1322: }
1323:
1324: static void
1325: md_post_Pf(struct roff_node *n)
1326: {
1327: if (n->next != NULL && (n->next->flags & NODE_LINE) == 0)
1328: outflags &= ~MD_spc;
1329: }
1330:
1331: static int
1332: md_pre_Pp(struct roff_node *n)
1333: {
1334: outflags |= MD_sp;
1335: return 0;
1336: }
1337:
1338: static int
1339: md_pre_Rs(struct roff_node *n)
1340: {
1341: if (n->sec == SEC_SEE_ALSO)
1342: outflags |= MD_sp;
1343: return 1;
1344: }
1345:
1346: static int
1347: md_pre_Sh(struct roff_node *n)
1348: {
1349: switch (n->type) {
1350: case ROFFT_HEAD:
1351: outflags |= MD_sp;
1352: md_rawword(n->tok == MDOC_Sh ? "#" : "##");
1353: break;
1354: case ROFFT_BODY:
1355: outflags |= MD_sp;
1356: break;
1357: default:
1358: break;
1359: }
1360: return 1;
1361: }
1362:
1363: static int
1364: md_pre_Sm(struct roff_node *n)
1365: {
1366: if (n->child == NULL)
1367: outflags ^= MD_Sm;
1368: else if (strcmp("on", n->child->string) == 0)
1369: outflags |= MD_Sm;
1370: else
1371: outflags &= ~MD_Sm;
1372:
1373: if (outflags & MD_Sm)
1374: outflags |= MD_spc;
1375:
1376: return 0;
1377: }
1378:
1379: static int
1380: md_pre_Vt(struct roff_node *n)
1381: {
1382: switch (n->type) {
1383: case ROFFT_BLOCK:
1384: md_pre_syn(n);
1385: return 1;
1386: case ROFFT_BODY:
1387: case ROFFT_ELEM:
1388: md_pre_raw(n);
1389: return 1;
1390: default:
1391: return 0;
1392: }
1393: }
1394:
1395: static void
1396: md_post_Vt(struct roff_node *n)
1397: {
1398: switch (n->type) {
1399: case ROFFT_BODY:
1400: case ROFFT_ELEM:
1401: md_post_raw(n);
1402: break;
1403: default:
1404: break;
1405: }
1406: }
1407:
1408: static int
1409: md_pre_Xr(struct roff_node *n)
1410: {
1411: n = n->child;
1412: if (n == NULL)
1413: return 0;
1414: md_node(n);
1415: n = n->next;
1416: if (n == NULL)
1417: return 0;
1418: outflags &= ~MD_spc;
1419: md_word("(");
1420: md_node(n);
1421: md_word(")");
1422: return 0;
1423: }
1424:
1425: static int
1426: md_pre__T(struct roff_node *n)
1427: {
1.2 schwarze 1428: if (n->parent->tok == MDOC_Rs && n->parent->norm->Rs.quote_T)
1.1 schwarze 1429: md_word("\"");
1430: else
1431: md_rawword("*");
1432: outflags &= ~MD_spc;
1433: return 1;
1434: }
1435:
1436: static void
1437: md_post__T(struct roff_node *n)
1438: {
1439: outflags &= ~MD_spc;
1.2 schwarze 1440: if (n->parent->tok == MDOC_Rs && n->parent->norm->Rs.quote_T)
1.1 schwarze 1441: md_word("\"");
1442: else
1443: md_rawword("*");
1444: md_post_pc(n);
1445: }
1446:
1447: static int
1448: md_pre_br(struct roff_node *n)
1449: {
1450: outflags |= MD_br;
1451: return 0;
1452: }