The Perl Toolchain Summit needs more sponsors. If your company depends on Perl, please support this very important event.
/* markdown: a C implementation of John Gruber's Markdown markup language.
 *
 * Copyright (C) 2010 David L Parsons.
 * The redistribution terms are provided in the COPYRIGHT file that must
 * be distributed with this source code.
 */
#include <stdio.h>
#include <string.h>
#include <stdarg.h>
#include <stdlib.h>
#include <time.h>
#include <ctype.h>

#include "config.h"

#include "cstring.h"
#include "markdown.h"
#include "amalloc.h"


/* emmatch: the emphasis mangler that's run after a block
 *          of html has been generated.
 *
 *          It should create MarkdownTest_1.0 (and _1.0.3)
 *          compatable emphasis for non-pathological cases
 *          and it should fail in a standards-compliant way
 *          when someone attempts to feed it junk.
 *
 *          Emmatching is done after the input has been 
 *          processed into a STRING (f->Q) of text and
 *          emphasis blocks.   After ___mkd_emblock() finishes,
 *          it truncates f->Q and leaves the rendered paragraph
 *          if f->out.
 */


/* empair() -- find the NEAREST matching emphasis token (or
 *             subtoken of a 3+ long emphasis token.
 */
static int
empair(MMIOT *f, int first, int last, int match)
{
    
    int i;
    block *begin, *p;

    begin = &T(f->Q)[first];

    for (i=first+1; i <= last; i++) {
	p = &T(f->Q)[i];

	if ( (p->b_type != bTEXT) && (p->b_count <= 0) )
	    continue; /* break? */
	
	if ( p->b_type == begin->b_type ) {
	    if ( p->b_count == match )	/* exact match */
		return i;

	    if ( p->b_count > 2 )	/* fuzzy match */
		return i;
	}
    }
    return 0;
} /* empair */


/* emfill() -- if an emphasis token has leftover stars or underscores,
 *             convert them back into character and append them to b_text.
 */
static void
emfill(block *p)
{
    int j;

    if ( p->b_type == bTEXT )
	return;
	
    for (j=0; j < p->b_count; j++)
	  EXPAND(p->b_text) = p->b_char;
    p->b_count = 0;
} /* emfill */


static void
emclose(MMIOT *f, int first, int last)
{
    int j;

    for (j=first+1; j<last-1; j++)
	emfill(&T(f->Q)[j]);
}


static struct emtags {
    char open[10];
    char close[10];
    int size;
} emtags[] = {  { "<em>" , "</em>", 5 }, { "<strong>", "</strong>", 9 } };


static void emblock(MMIOT*,int,int);


/* emmatch() -- match emphasis for a single emphasis token.
 */
static void
emmatch(MMIOT *f, int first, int last)
{
    block *start = &T(f->Q)[first];
    int e, e2, match;

    switch (start->b_count) {
    case 2: if ( e = empair(f,first,last,match=2) )
		break;
    case 1: e = empair(f,first,last,match=1);
	    break;
    case 0: return;
    default:
	    e = empair(f,first,last,1);
	    e2= empair(f,first,last,2);

	    if ( e2 >= e ) {
		e = e2;
		match = 2;
	    } 
	    else
		match = 1;
	    break;
    }

    if ( e ) {
	/* if we found emphasis to match, match it, recursively call
	 * emblock to match emphasis inside the new html block, add
	 * the emphasis markers for the block, then (tail) recursively
	 * call ourself to match any remaining emphasis on this token.
	 */
	block *end = &T(f->Q)[e];

	end->b_count -= match;
	start->b_count -= match;

	emblock(f, first, e);

	PREFIX(start->b_text, emtags[match-1].open, emtags[match-1].size-1);
	SUFFIX(end->b_post, emtags[match-1].close, emtags[match-1].size);

	emmatch(f, first, last);
    }
} /* emmatch */


/* emblock() -- walk a blocklist, attempting to match emphasis
 */
static void
emblock(MMIOT *f, int first, int last)
{
    int i;
    
    for ( i = first; i <= last; i++ )
	if ( T(f->Q)[i].b_type != bTEXT )
	    emmatch(f, i, last);
    emclose(f, first, last);
} /* emblock */


/* ___mkd_emblock() -- emblock a string of blocks, then concatenate the
 *                     resulting text onto f->out.
 */
void
___mkd_emblock(MMIOT *f)
{
    int i;
    block *p;

    emblock(f, 0, S(f->Q)-1);
    
    for (i=0; i < S(f->Q); i++) {
	p = &T(f->Q)[i];
	emfill(p);
	
	if ( S(p->b_post) ) { SUFFIX(f->out, T(p->b_post), S(p->b_post));
			      DELETE(p->b_post); }
	if ( S(p->b_text) ) { SUFFIX(f->out, T(p->b_text), S(p->b_text));
			      DELETE(p->b_text); }
    }
    
    S(f->Q) = 0;
} /* ___mkd_emblock */