#include <assert.h>
#include <stdio.h>
#include <string.h>

#include "houdini.h"

#define ESCAPE_GROW_FACTOR( x ) ( ( (x) * 12 ) / 10 )    /* this is very scientific, yes */

/**
 * According to the OWASP rules:
 *
 * & --> &amp;
 * < --> &lt;
 * > --> &gt;
 * " --> &quot;
 * ' --> &#x27;     &apos; is not recommended
 * / --> &#x2F;     forward slash is included as it helps end an HTML entity
 *
 */
static const char HTML_ESCAPE_TABLE[] =
{
    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
    0, 0, 1, 0, 0, 0, 2, 3, 0, 0, 0, 0, 0, 0, 0, 4,
    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 5, 0, 6, 0,
    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
};

static const char* HTML_ESCAPES[] =
{
    "",
    "&quot;",
    "&amp;",
    "&#39;",
    "&#47;",
    "&lt;",
    "&gt;"
};

void houdini_escape_html0( struct buf* ob, const uint8_t* src, size_t size, int secure )
{
    size_t i = 0, org, esc = 0;

    bufgrow( ob, ESCAPE_GROW_FACTOR( size ) );

    while( i < size )
    {
        org = i;

        while( i < size && (esc = HTML_ESCAPE_TABLE[src[i]]) == 0 )
            i++;

        if( i > org )
            bufput( ob, src + org, i - org );

        /* escaping */
        if( i >= size )
            break;

        /* The forward slash is only escaped in secure mode */
        if( src[i] == '/' && !secure )
        {
            bufputc( ob, '/' );
        }
        else
        {
            bufputs( ob, HTML_ESCAPES[esc] );
        }

        i++;
    }
}


void houdini_escape_html( struct buf* ob, const uint8_t* src, size_t size )
{
    houdini_escape_html0( ob, src, size, 1 );
}
