ext/generate.c in rdiscount-2.2.0.2 vs ext/generate.c in rdiscount-2.2.7

- old
+ new

@@ -54,20 +54,20 @@ peek(MMIOT *f, int i) { i += (f->isp-1); - return (i >= 0) && (i < S(f->in)) ? T(f->in)[i] : EOF; + return (i >= 0) && (i < S(f->in)) ? (unsigned char)T(f->in)[i] : EOF; } /* pull a byte from the input buffer */ -static inline int +static inline unsigned int pull(MMIOT *f) { - return ( f->isp < S(f->in) ) ? T(f->in)[f->isp++] : EOF; + return ( f->isp < S(f->in) ) ? (unsigned char)T(f->in)[f->isp++] : EOF; } /* return a pointer to the current position in the input buffer. */ @@ -106,12 +106,14 @@ return isthisspace(f, i) || ispunct(peek(f,i)); } /* return/set the current cursor position + * (when setting the current cursor position we also need to flush the + * last character written cache) */ -#define mmiotseek(f,x) (f->isp = x) +#define mmiotseek(f,x) ((f->isp = x), (f->last = 0)) #define mmiottell(f) (f->isp) /* move n characters forward ( or -n characters backward) in the input buffer. */ @@ -127,21 +129,21 @@ */ static void Qchar(int c, MMIOT *f) { block *cur; - + if ( S(f->Q) == 0 ) { cur = &EXPAND(f->Q); memset(cur, 0, sizeof *cur); cur->b_type = bTEXT; } else cur = &T(f->Q)[S(f->Q)-1]; EXPAND(cur->b_text) = c; - + } /* Qstring() */ @@ -176,10 +178,20 @@ va_end(ptr); Qstring(bfr, f); } +/* Qanchor() prints out a suitable-for-id-tag version of a string + */ +static void +Qanchor(struct line *p, MMIOT *f) +{ + mkd_string_to_anchor(T(p->text), S(p->text), + (mkd_sta_function_t)Qchar, f, 1, f); +} + + /* Qem() */ static void Qem(MMIOT *f, char c, int count) { @@ -195,17 +207,17 @@ /* generate html from a markup fragment */ void -___mkd_reparse(char *bfr, int size, int flags, MMIOT *f, char *esc) +___mkd_reparse(char *bfr, int size, mkd_flag_t flags, MMIOT *f, char *esc) { MMIOT sub; struct escaped e; ___mkd_initmmiot(&sub, f->footnotes); - + sub.flags = f->flags | flags; sub.cb = f->cb; sub.ref_prefix = f->ref_prefix; if ( esc ) { @@ -217,15 +229,20 @@ sub.esc = f->esc; push(bfr, size, &sub); pushc(0, &sub); S(sub.in)--; - + text(&sub); ___mkd_emblock(&sub); - + Qwrite(T(sub.out), S(sub.out), f); + /* inherit the last character printed from the reparsed + * text; this way superscripts can work when they're + * applied to something embedded in a link + */ + f->last = sub.last; ___mkd_freemmiot(&sub, f->footnotes); } @@ -261,11 +278,11 @@ c = *s++; if ( !( ispunct(c) || isspace(c) ) ) Qchar('\\', f); } - + if ( c == '&' ) Qstring("&amp;", f); else if ( c == '<' ) Qstring("&lt;", f); else if ( c == '"' ) @@ -429,11 +446,11 @@ good=( c == ')' ); if ( good ) { if ( peek(f, 1) == ')' ) pull(f); - + ___mkd_tidy(&p->link); } return good; } /* linkybroket */ @@ -454,11 +471,11 @@ if ( (c = eatspace(f)) == EOF ) return 0; if ( c == '<' ) { pull(f); - if ( !(f->flags & MKD_1_COMPAT) ) + if ( !is_flag_set(f->flags, MKD_1_COMPAT) ) return linkybroket(f,image,p); mayneedtotrim=1; } T(p->link) = cursor(f); @@ -475,16 +492,16 @@ } pull(f); } if ( peek(f, 1) == ')' ) pull(f); - + ___mkd_tidy(&p->link); - + if ( mayneedtotrim && (T(p->link)[S(p->link)-1] == '>') ) --S(p->link); - + return 1; } @@ -576,16 +593,16 @@ */ static void printlinkyref(MMIOT *f, linkytype *tag, char *link, int size) { char *edit; - - if ( f->flags & IS_LABEL ) + + if ( is_flag_set(f->flags, IS_LABEL) ) return; - + Qstring(tag->link_pfx, f); - + if ( tag->kind & IS_URL ) { if ( f->cb && f->cb->e_url && (edit = (*f->cb->e_url)(link, size, f->cb->e_data)) ) { puturl(edit, strlen(edit), f, 0); if ( f->cb->e_free ) (*f->cb->e_free)(edit, f->cb->e_data); } @@ -621,11 +638,11 @@ static int extra_linky(MMIOT *f, Cstring text, Footnote *ref) { if ( ref->flags & REFERENCED ) return 0; - + if ( f->flags & IS_LABEL ) ___mkd_reparse(T(text), S(text), linkt.flags, f, 0); else { ref->flags |= REFERENCED; ref->refnumber = ++ f->footnotes->reference; @@ -635,10 +652,36 @@ } return 1; } /* extra_linky */ + +/* check a url (or url fragment to see that it begins with a known good + * protocol (or no protocol at all) + */ +static int +safelink(Cstring link) +{ + char *p, *colon; + + if ( T(link) == 0 ) /* no link; safe */ + return 1; + + p = T(link); + if ( (colon = memchr(p, ':', S(link))) == 0 ) + return 1; /* no protocol specified: safe */ + + if ( !isalpha(*p) ) /* protocol/method is [alpha][alnum or '+.-'] */ + return 1; + while ( ++p < colon ) + if ( !(isalnum(*p) || *p == '.' || *p == '+' || *p == '-') ) + return 1; + + return isautoprefix(T(link), S(link)); +} + + /* print out a linky (or fail if it's Not Allowed) */ static int linkyformat(MMIOT *f, Cstring text, int image, Footnote *ref) { @@ -646,27 +689,25 @@ if ( image ) tag = &imaget; else if ( tag = pseudo(ref->link) ) { - if ( f->flags & (MKD_NO_EXT|MKD_SAFELINK) ) + if ( is_flag_set(f->flags, MKD_NO_EXT) || is_flag_set(f->flags, MKD_SAFELINK) ) return 0; } - else if ( (f->flags & MKD_SAFELINK) && T(ref->link) - && (T(ref->link)[0] != '/') - && !isautoprefix(T(ref->link), S(ref->link)) ) + else if ( is_flag_set(f->flags, MKD_SAFELINK) && !safelink(ref->link) ) /* if MKD_SAFELINK, only accept links that are local or * a well-known protocol */ return 0; else tag = &linkt; if ( f->flags & tag->flags ) return 0; - if ( f->flags & IS_LABEL ) + if ( is_flag_set(f->flags, IS_LABEL) ) ___mkd_reparse(T(text), S(text), tag->flags, f, 0); else if ( tag->link_pfx ) { printlinkyref(f, tag, T(ref->link), S(ref->link)); if ( tag->WxH ) { @@ -698,11 +739,11 @@ linkylinky(int image, MMIOT *f) { int start = mmiottell(f); Cstring name; Footnote key, *ref; - + int status = 0; int extra_footnote = 0; CREATE(name); memset(&key, 0, sizeof key); @@ -716,26 +757,26 @@ else { int goodlink, implicit_mark = mmiottell(f); if ( isspace(peek(f,1)) ) pull(f); - + if ( peek(f,1) == '[' ) { pull(f); /* consume leading '[' */ goodlink = linkylabel(f, &key.tag); } else { /* new markdown implicit name syntax doesn't * require a second [] */ mmiotseek(f, implicit_mark); - goodlink = !(f->flags & MKD_1_COMPAT); + goodlink = !is_flag_set(f->flags, MKD_1_COMPAT); - if ( (f->flags & MKD_EXTRA_FOOTNOTE) && (!image) && S(name) && T(name)[0] == '^' ) + if ( is_flag_set(f->flags, MKD_EXTRA_FOOTNOTE) && (!image) && S(name) && T(name)[0] == '^' ) extra_footnote = 1; } - + if ( goodlink ) { if ( !S(key.tag) ) { DELETE(key.tag); T(key.tag) = T(name); S(key.tag) = S(name); @@ -775,11 +816,11 @@ case '<': Qstring("&lt;", f); break; default : Qchar(c, f); break; } } - + /* * convert an email address to a string of nonsense */ static void mangle(char *s, int len, MMIOT *f) @@ -817,11 +858,11 @@ static int matchticks(MMIOT *f, int tickchar, int ticks, int *endticks) { int size, count, c; int subsize=0, subtick=0; - + *endticks = ticks; for (size = 0; (c=peek(f,size+ticks)) != EOF; size ++) { if ( (c == tickchar) && ( count = nrticks(size+ticks,tickchar,f)) ) { if ( count == ticks ) return size; @@ -850,11 +891,11 @@ code(MMIOT *f, char *s, int length) { int i,c; for ( i=0; i < length; i++ ) - if ( (c = s[i]) == MKD_EOLN) /* ^C: expand back to 2 spaces */ + if ( (c = s[i]) == MKD_EOLN) /* expand back to 2 spaces */ Qstring(" ", f); else if ( c == '\\' && (i < length-1) && escaped(f, s[i+1]) ) cputc(s[++i], f); else cputc(c, f); @@ -868,22 +909,57 @@ Qstring("<del>", f); ___mkd_reparse(cursor(f)-1, size, 0, f, 0); Qstring("</del>", f); } +#ifdef TYPORA +/* subspan() -- write out a chunk of text, blocking with <sub>...</sub> + */ +static void +subspan(MMIOT *f, int size) +{ + Qstring("<sub>", f); + ___mkd_reparse(cursor(f)-1, size, 0, f, 0); + Qstring("</sub>", f); +} + +/* supspan() -- write out a chunk of text, blocking with <sup>...</sup> + */ +static void +supspan(MMIOT *f, int size) +{ + Qstring("<sup>", f); + ___mkd_reparse(cursor(f)-1, size, 0, f, 0); + Qstring("</sup>", f); +} + + +/* highlightspan() -- write out a chunk of text, blocking with <mark>...</mark> + */ +static void +highlightspan(MMIOT *f, int size) +{ + Qstring("<mark>", f); + ___mkd_reparse(cursor(f)-1, size, 0, f, 0); + Qstring("</mark>", f); +} +#endif + + + /* codespan() -- write out a chunk of text as code, trimming one * space off the front and/or back as appropriate. */ static void codespan(MMIOT *f, int size) { int i=0; if ( size > 1 && peek(f, size-1) == ' ' ) --size; if ( peek(f,i) == ' ' ) ++i, --size; - + Qstring("<code>", f); code(f, cursor(f)+(i-1), size); Qstring("</code>", f); } /* codespan */ @@ -894,16 +970,16 @@ static int forbidden_tag(MMIOT *f) { int c = toupper(peek(f, 1)); - if ( f->flags & MKD_NOHTML ) + if ( is_flag_set(f->flags, MKD_NOHTML) ) return 1; - if ( c == 'A' && (f->flags & MKD_NOLINKS) && !isthisalnum(f,2) ) + if ( c == 'A' && is_flag_set(f->flags, MKD_NOLINKS) && !isthisalnum(f,2) ) return 1; - if ( c == 'I' && (f->flags & MKD_NOIMAGE) + if ( c == 'I' && is_flag_set(f->flags, MKD_NOIMAGE) && strncasecmp(cursor(f)+1, "MG", 2) == 0 && !isthisalnum(f,4) ) return 1; return 0; } @@ -916,21 +992,21 @@ */ static int maybe_address(char *p, int size) { int ok = 0; - + for ( ;size && (isalnum(*p) || strchr("._-+*", *p)); ++p, --size) ; if ( ! (size && *p == '@') ) return 0; - + --size, ++p; if ( size && *p == '.' ) return 0; - + for ( ;size && (isalnum(*p) || strchr("._-+", *p)); ++p, --size ) if ( *p == '.' && size > 1 ) ok = 1; return size ? 0 : ok; } @@ -945,13 +1021,13 @@ process_possible_link(MMIOT *f, int size) { int address= 0; int mailto = 0; char *text = cursor(f); - - if ( f->flags & MKD_NOLINKS ) return 0; + if ( is_flag_set(f->flags, MKD_NOLINKS) ) return 0; + if ( (size > 7) && strncasecmp(text, "mailto:", 7) == 0 ) { /* if it says it's a mailto, it's a mailto -- who am * I to second-guess the user? */ address = 1; @@ -981,67 +1057,80 @@ } return 0; } /* process_possible_link */ +/* + * check if a character is one of the things the reference implementation considers valid for starting + * a html(ish) tag + */ +static inline int +is_a_strict_tag_prefix(int c) +{ + return isalpha(c) || (c == '/') || (c == '!') || (c == '$') || (c == '?'); +} + + /* a < may be just a regular character, the start of an embedded html * tag, or the start of an <automatic link>. If it's an automatic * link, we also need to know if it's an email address because if it * is we need to mangle it in our futile attempt to cut down on the * spaminess of the rendered page. */ static int maybe_tag_or_link(MMIOT *f) { - int c, size; - int maybetag = 1; + int c, size=0; - if ( f->flags & MKD_TAGTEXT ) + if ( is_flag_set(f->flags, MKD_TAGTEXT) ) return 0; - for ( size=0; (c = peek(f, size+1)) != '>'; size++) { - if ( c == EOF ) - return 0; - else if ( c == '\\' ) { - maybetag=0; - if ( peek(f, size+2) != EOF ) - size++; + c = peek(f, 1); + + + if ( is_a_strict_tag_prefix(c) ) { + /* By decree of Markdown.pl *this is a tag* and we want to absorb everything up + * to the next '>', unless interrupted by another '<' OR a '`', at which point + * we kick it back to the caller as plain old text. + */ + size=1; + while ( (c=peek(f,size+1)) != '>' ) { + if ( c == EOF || c == '<' ) + return 0; + if ( is_flag_set(f->flags, MKD_STRICT) ) { + if ( c == '`' ) + return 0; + } + size++; } - else if ( isspace(c) ) - break; - else if ( ! (c == '/' - || (f->flags & MKD_GITHUBTAGS && (c == '-' || c == '_')) - || isalnum(c) ) ) - maybetag=0; } - if ( size ) { - if ( maybetag || (size >= 3 && strncmp(cursor(f), "!--", 3) == 0) ) { + if ( size > 0 ) { + if ( process_possible_link(f, size) ) { + shift(f, size+1); + return 1; + } + else { + int i; - /* It is not a html tag unless we find the closing '>' in - * the same block. - */ - while ( (c = peek(f, size+1)) != '>' ) - if ( c == EOF ) - return 0; - else - size++; - if ( forbidden_tag(f) ) return 0; - Qchar('<', f); - while ( ((c = peek(f, 1)) != EOF) && (c != '>') ) - Qchar(pull(f), f); - return 1; - } - else if ( !isspace(c) && process_possible_link(f, size) ) { + for ( i=0; i <= size+1; i++ ) { + c = peek(f,i); + + if ( (c == '&') && (i > 0) ) + Qstring("&amp;", f); + else + Qchar(c, f); + } + shift(f, size+1); return 1; } } - + return 0; } /* autolinking means that all inline html is <a href'ified>. A @@ -1161,11 +1250,13 @@ static int smartypants(int c, int *flags, MMIOT *f) { int i; - if ( f->flags & (MKD_NOPANTS|MKD_TAGTEXT|IS_LABEL) ) + if ( is_flag_set(f->flags, MKD_NOPANTS) + || is_flag_set(f->flags, MKD_TAGTEXT) + || is_flag_set(f->flags, IS_LABEL) ) return 0; for ( i=0; i < NRSMART; i++) if ( (c == smarties[i].c0) && islike(f, smarties[i].pat) ) { if ( smarties[i].entity ) @@ -1205,11 +1296,10 @@ } return 0; } /* smartypants */ -#if WITH_LATEX /* process latex with arbitrary 2-character ( $$ .. $$, \[ .. \], \( .. \) * delimiters */ static int mathhandler(MMIOT *f, int e1, int e2) @@ -1225,11 +1315,10 @@ return 1; } } return 0; } -#endif /* process a body of text encased in some sort of tick marks. If it * works, generate the output and return 1, otherwise just return 0 and * let the caller figure it out. @@ -1255,22 +1344,22 @@ return 1; } return 0; } -#define tag_text(f) (f->flags & MKD_TAGTEXT) +#define tag_text(f) is_flag_set(f->flags, MKD_TAGTEXT) static void text(MMIOT *f) { int c, j; int rep; int smartyflags = 0; while (1) { - if ( (f->flags & MKD_AUTOLINK) && isalpha(peek(f,1)) && !tag_text(f) ) + if ( is_flag_set(f->flags, MKD_AUTOLINK) && isalpha(peek(f,1)) && !tag_text(f) ) maybe_autolink(f); c = pull(f); if (c == EOF) @@ -1294,11 +1383,11 @@ case '"': if ( tag_text(f) ) Qstring("&quot;", f); else Qchar(c, f); break; - + case '!': if ( peek(f,1) == '[' ) { pull(f); if ( tag_text(f) || !linkylinky(1, f) ) Qstring("![", f); } @@ -1307,14 +1396,35 @@ break; case '[': if ( tag_text(f) || !linkylinky(0, f) ) Qchar(c, f); break; + +#ifdef TYPORA + case '=': if ( is_flag_set(f->flags, MKD_NOSUPERSCRIPT) + || is_flag_set(f->flags, MKD_STRICT) + || is_flag_set(f->flags, MKD_TAGTEXT) + || ! tickhandler(f,c,2,0, highlightspan)) + Qchar(c, f); + break; + + /* A^B^ -> A<sup>B</sup> */ + case '^': if ( is_flag_set(f->flags, MKD_NOSUPERSCRIPT) + || is_flag_set(f->flags, MKD_STRICT) + || is_flag_set(f->flags, MKD_TAGTEXT) + || ! tickhandler(f,c,1,0, supspan)) + Qchar(c, f); + break; +#else /* !TYPORA */ /* A^B -> A<sup>B</sup> */ - case '^': if ( (f->flags & (MKD_NOSUPERSCRIPT|MKD_STRICT|MKD_TAGTEXT)) - || (isthisnonword(f,-1) && peek(f,-1) != ')') - || isthisspace(f,1) ) + case '^': if ( is_flag_set(f->flags, MKD_NOSUPERSCRIPT) + || is_flag_set(f->flags, MKD_STRICT) + || is_flag_set(f->flags, MKD_TAGTEXT) + || (f->last == 0) + || ((ispunct(f->last) || isspace(f->last)) + && f->last != ')') + || isthisspace(f,1) ) Qchar(c,f); else { char *sup = cursor(f); int len = 0; @@ -1341,15 +1451,15 @@ Qstring("<sup>",f); ___mkd_reparse(sup, len, 0, f, "()"); Qstring("</sup>", f); } break; +#endif /* TYPORA */ case '_': /* Underscores don't count if they're in the middle of a word */ - if ( !(f->flags & (MKD_NORELAXED|MKD_STRICT)) - && isthisalnum(f,-1) - && isthisalnum(f,1) ) { + if ( !(is_flag_set(f->flags, MKD_NORELAXED) || is_flag_set(f->flags, MKD_STRICT)) + && isthisalnum(f,-1) && isthisalnum(f,1) ) { Qchar(c, f); break; } case '*': /* Underscores & stars don't count if they're out in the middle @@ -1365,12 +1475,21 @@ for (rep = 1; peek(f,1) == c; pull(f) ) ++rep; Qem(f,c,rep); } break; - - case '~': if ( (f->flags & (MKD_NOSTRIKETHROUGH|MKD_TAGTEXT|MKD_STRICT)) || ! tickhandler(f,c,2,0, delspan) ) + +#ifdef TYPORA +#define ticktick(f,c) (tickhandler(f,c,2,0,delspan) || tickhandler(f,c,1,0,subspan)) +#else +#define ticktick(f,c) tickhandler(f,c,2,0,delspan) +#endif + + case '~': if ( is_flag_set(f->flags, MKD_NOSTRIKETHROUGH) + || is_flag_set(f->flags, MKD_STRICT) + || is_flag_set(f->flags, MKD_TAGTEXT) + || !ticktick(f,c) ) Qchar(c, f); break; case '`': if ( tag_text(f) || !tickhandler(f,c,1,1,codespan) ) Qchar(c, f); @@ -1386,39 +1505,39 @@ /* Markdown.pl does not escape <[nonwhite] * sequences */ Qchar('\\', f); shift(f, -1); } - + break; - case '^': if ( f->flags & (MKD_STRICT|MKD_NOSUPERSCRIPT) ) { + case '^': if ( is_flag_set(f->flags, MKD_STRICT) + || is_flag_set(f->flags, MKD_NOSUPERSCRIPT) ) { Qchar('\\', f); shift(f,-1); break; } Qchar(c, f); break; - + case ':': case '|': - if ( f->flags & MKD_NOTABLES ) { + if ( is_flag_set(f->flags, MKD_NOTABLES) ) { Qchar('\\', f); shift(f,-1); break; } Qchar(c, f); break; - + case EOF: Qchar('\\', f); break; -#if WITH_LATEX case '[': - case '(': if ( mathhandler(f, '\\', (c =='(')?')':']') ) + case '(': if ( is_flag_set(f->flags, MKD_LATEX) + && mathhandler(f, '\\', (c =='(')?')':']') ) break; /* else fall through to default */ -#endif - + default: if ( escaped(f,c) || strchr(">#.-+{}]![*_\\()`", c) ) Qchar(c, f); else { Qchar('\\', f); @@ -1426,12 +1545,16 @@ } break; } break; - case '<': if ( !maybe_tag_or_link(f) ) - Qstring("&lt;", f); + case '<': if ( !maybe_tag_or_link(f) ) { + if ( is_flag_set(f->flags, MKD_STRICT) && is_a_strict_tag_prefix(peek(f,1)) ) + Qchar(c, f); + else + Qstring("&lt;", f); + } break; case '&': j = (peek(f,1) == '#' ) ? 2 : 1; while ( isthisalnum(f,j) ) ++j; @@ -1440,21 +1563,20 @@ Qstring("&amp;", f); else Qchar(c, f); break; -#if WITH_LATEX - case '$': if ( peek(f, 1) == '$' ) { + case '$': if ( is_flag_set(f->flags, MKD_LATEX) && (peek(f, 1) == '$') ) { pull(f); if ( mathhandler(f, '$', '$') ) break; Qchar('$', f); } /* fall through to default */ -#endif - - default: Qchar(c, f); + + default: f->last = c; + Qchar(c, f); break; } } /* truncate the input string after we've finished processing it */ S(f->in) = f->isp = 0; @@ -1464,26 +1586,22 @@ /* print a header block */ static void printheader(Paragraph *pp, MMIOT *f) { - if ( f->flags & MKD_IDANCHOR ) { + if ( is_flag_set(f->flags, MKD_IDANCHOR) ) { Qprintf(f, "<h%d", pp->hnumber); - if ( f->flags & MKD_TOC ) { + if ( is_flag_set(f->flags, MKD_TOC) ) { Qstring(" id=\"", f); - mkd_string_to_anchor(T(pp->text->text), - S(pp->text->text), - (mkd_sta_function_t)Qchar, f, 1, f->flags); + Qanchor(pp->text, f); Qchar('"', f); } Qchar('>', f); } else { - if ( f->flags & MKD_TOC ) { + if ( is_flag_set(f->flags, MKD_TOC) ) { Qstring("<a name=\"", f); - mkd_string_to_anchor(T(pp->text->text), - S(pp->text->text), - (mkd_sta_function_t)Qchar, f, 1, f->flags); + Qanchor(pp->text, f); Qstring("\"></a>\n", f); } Qprintf(f, "<h%d>", pp->hnumber); } push(T(pp->text->text), S(pp->text->text), f); @@ -1509,11 +1627,11 @@ ___mkd_tidy(&p->text); if ( T(p->text)[S(p->text)-1] == '|' ) --S(p->text); - + Qstring("<tr>\n", f); while ( idx < S(p->text) ) { first = idx; if ( force && (colno >= S(align)-1) ) idx = S(p->text); @@ -1570,11 +1688,11 @@ CREATE(align); for (p=T(dash->text), start=dash->dle; start < S(dash->text); ) { char first, last; int end; - + last=first=0; for (end=start ; (end < S(dash->text)) && p[end] != '|'; ++ end ) { if ( p[end] == '\\' ) ++ end; else if ( !isspace(p[end]) ) { @@ -1612,13 +1730,14 @@ static int printblock(Paragraph *pp, MMIOT *f) { - Line *t = pp->text; static char *Begin[] = { "", "<p>", "<p style=\"text-align:center;\">" }; static char *End[] = { "", "</p>","</p>" }; + Line *t = pp->text; + int align = pp->align; while (t) { if ( S(t->text) ) { if ( t->next && S(t->text) > 2 && T(t->text)[S(t->text)-2] == ' ' @@ -1634,24 +1753,60 @@ pushc('\n', f); } } t = t->next; } - Qstring(Begin[pp->align], f); + Qstring(Begin[align], f); text(f); - Qstring(End[pp->align], f); + Qstring(End[align], f); return 1; } static void printcode(Line *t, char *lang, MMIOT *f) { int blanks; + if ( f->cb->e_codefmt ) { + /* external code block formatter; copy the text into a buffer, + * call the formatter to style it, then dump that styled text + * directly to the queue + */ + char *text; + char *fmt; + int size, copy_p; + Line *p; + + for (size=0, p = t; p; p = p->next ) + size += 1+S(p->text); + + text = malloc(1+size); + + for ( copy_p = 0; t ; t = t->next ) { + memcpy(text+copy_p, T(t->text), S(t->text)); + copy_p += S(t->text); + text[copy_p++] = '\n'; + } + text[copy_p] = 0; + + fmt = (*(f->cb->e_codefmt))(text, copy_p, (lang && lang[0]) ? lang : 0); + free(text); + + if ( fmt ) { + Qwrite(fmt, strlen(fmt), f); + if ( f->cb->e_free ) + (*(f->cb->e_free))(fmt, f->cb->e_data); + return; + } + /* otherwise the external formatter failed and we need to + * fall back to the traditional codeblock format + */ + } + Qstring("<pre><code", f); - if (lang) { + if (lang && lang[0]) { Qstring(" class=\"", f); Qstring(lang, f); Qstring("\"", f); } Qstring(">", f); @@ -1672,11 +1827,11 @@ static void printhtml(Line *t, MMIOT *f) { int blanks; - + for ( blanks=0; t ; t = t->next ) if ( S(t->text) ) { for ( ; blanks; --blanks ) Qchar('\n', f); @@ -1687,23 +1842,63 @@ blanks++; } static void -htmlify(Paragraph *p, char *block, char *arguments, MMIOT *f) +htmlify_paragraphs(Paragraph *p, MMIOT *f) { ___mkd_emblock(f); - if ( block ) - Qprintf(f, arguments ? "<%s %s>" : "<%s>", block, arguments); - ___mkd_emblock(f); while (( p = display(p, f) )) { ___mkd_emblock(f); Qstring("\n\n", f); } +} + +#ifdef GITHUB_CHECKBOX +static void +li_htmlify(Paragraph *p, char *arguments, mkd_flag_t flags, MMIOT *f) +{ + ___mkd_emblock(f); + + Qprintf(f, "<li"); + if ( arguments ) + Qprintf(f, " %s", arguments); + if ( flags & GITHUB_CHECK ) + Qprintf(f, " class=\"github_checkbox\""); + Qprintf(f, ">"); +#if CHECKBOX_AS_INPUT + if ( flags & GITHUB_CHECK ) { + Qprintf(f, "<input disabled=\"\" type=\"checkbox\""); + if ( flags & IS_CHECKED ) + Qprintf(f, " checked=\"checked\""); + Qprintf(f, "/>"); + } +#else + if ( flags & GITHUB_CHECK ) + Qprintf(f, flags & IS_CHECKED ? "&#x2611;" : "&#x2610;"); +#endif + + htmlify_paragraphs(p, f); + + Qprintf(f, "</li>"); + ___mkd_emblock(f); +} +#endif + + +static void +htmlify(Paragraph *p, char *block, char *arguments, MMIOT *f) +{ + ___mkd_emblock(f); if ( block ) + Qprintf(f, arguments ? "<%s %s>" : "<%s>", block, arguments); + + htmlify_paragraphs(p, f); + + if ( block ) Qprintf(f, "</%s>", block); ___mkd_emblock(f); } @@ -1739,11 +1934,15 @@ if ( typ == AL ) Qprintf(f, " type=\"a\""); Qprintf(f, ">\n"); for ( ; p ; p = p->next ) { +#ifdef GITHUB_CHECKBOX + li_htmlify(p->down, p->ident, p->flags, f); +#else htmlify(p->down, "li", p->ident, f); +#endif Qchar('\n', f); } Qprintf(f, "</%cl>\n", (typ==UL)?'u':'o'); } @@ -1754,28 +1953,28 @@ */ static Paragraph* display(Paragraph *p, MMIOT *f) { if ( !p ) return 0; - + switch ( p->typ ) { case STYLE: case WHITESPACE: break; case HTML: printhtml(p->text, f); break; - + case CODE: printcode(p->text, p->lang, f); break; - + case QUOTE: htmlify(p->down, p->ident ? "div" : "blockquote", p->ident, f); break; - + case UL: case OL: case AL: listdisplay(p->typ, p->down, f); break; @@ -1797,11 +1996,11 @@ break; case SOURCE: htmlify(p->down, 0, 0, f); break; - + default: printblock(p, f); break; } return p->next; @@ -1818,21 +2017,21 @@ if ( m->footnotes->reference == 0 ) return; Csprintf(&m->out, "\n<div class=\"footnotes\">\n<hr/>\n<ol>\n"); - + for ( i=1; i <= m->footnotes->reference; i++ ) { for ( j=0; j < S(m->footnotes->note); j++ ) { t = &T(m->footnotes->note)[j]; if ( (t->refnumber == i) && (t->flags & REFERENCED) ) { - Csprintf(&m->out, "<li id=\"%s:%d\">\n<p>", + Csprintf(&m->out, "<li id=\"%s:%d\">\n", p_or_nothing(m), t->refnumber); - Csreparse(&m->out, T(t->title), S(t->title), 0); + htmlify(t->text, 0, 0, m); Csprintf(&m->out, "<a href=\"#%sref:%d\" rev=\"footnote\">&#8617;</a>", p_or_nothing(m), t->refnumber); - Csprintf(&m->out, "</p></li>\n"); + Csprintf(&m->out, "</li>\n"); } } } Csprintf(&m->out, "</ol>\n</div>\n"); } @@ -1843,29 +2042,28 @@ */ int mkd_document(Document *p, char **res) { int size; - + if ( p && p->compiled ) { if ( ! p->html ) { htmlify(p->code, 0, 0, p->ctx); - if ( p->ctx->flags & MKD_EXTRA_FOOTNOTE ) + if ( is_flag_set(p->ctx->flags, MKD_EXTRA_FOOTNOTE) ) mkd_extra_footnotes(p->ctx); p->html = 1; size = S(p->ctx->out); - + if ( (size == 0) || T(p->ctx->out)[size-1] ) { /* Add a null byte at the end of the generated html, * but pretend it doesn't exist. */ EXPAND(p->ctx->out) = 0; --S(p->ctx->out); } } - + *res = T(p->ctx->out); return S(p->ctx->out); } return EOF; } -