/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
/*
//
// use-proc.c
//
// specialized markup processors
//
// Copyright (c) 1995-96 Jim Nelson.  Permission to distribute
// granted by the author.  No warranties are made on the fitness of this
// source code.
//
*/

#include "use-proc.h"

#include "defs.h"


uint UseProcessor(TASK *task, HTML_MARKUP *htmlMarkup, char **newPlaintext)
{
    const char *name;
    HTML_ATTRIBUTE *nameAttrib;
    const char *value;
    STREAM incfile;
    BOOL noexpand;
    int result;
    uint type;
    VARSTORE varstore;
    VARSTORE *topVarstore;

    nameAttrib = htmlMarkup->attrib;
    if(nameAttrib == NULL)
    {
        HtpMsg(MSG_ERROR, task->infile, "USE markup without parameter");
        return MARKUP_ERROR;
    }
    if (nameAttrib->value) {
        if (stricmp(nameAttrib->name, "NAME") == 0) {
            HtpMsg(MSG_WARNING, task->infile,
                   "deprecated USE syntax, see manual");
            name = nameAttrib->value;
        } else {
            HtpMsg(MSG_ERROR, task->infile, "illegal USE markup");
            return MARKUP_ERROR;
        }
    } else {
        name = nameAttrib->name;
    }
    htmlMarkup->attrib = nameAttrib->next;

    /* check if the noexpand flag is present */
    noexpand = UnlinkBoolAttributeInMarkup(htmlMarkup, "NOEXPAND");

    /* get the type of macro */
    type = GetVariableType(task->varstore, name);

    /* verify the macro exists and of correct type */
    if ((type != VAR_TYPE_SET_MACRO) && (type != VAR_TYPE_BLOCK_MACRO))
    {
        HtpMsg(MSG_ERROR, task->infile, 
               "macro %s has not been declared", name);
        DestroyAttribute(nameAttrib);
        FreeMemory(nameAttrib);
        return MARKUP_ERROR;
    }

    /* if more than one parameter is on the USE tag, then assume they are */
    /* local variables for the macro */
    if (htmlMarkup->attrib != NULL)
    {
        if(type == VAR_TYPE_SET_MACRO)
        {
            /* nope, not yet */
            HtpMsg(MSG_ERROR, task->infile, "macro parameters can only be used for BLOCK macros");
            DestroyAttribute(nameAttrib);
            FreeMemory(nameAttrib);
            return MARKUP_ERROR;
        }

        /* create a local variable store */
        if(InitializeVariableStore(&varstore) == FALSE)
        {
            HtpMsg(MSG_ERROR, task->infile, "unable to initialize local context for macro");
            DestroyAttribute(nameAttrib);
            FreeMemory(nameAttrib);
            return MARKUP_ERROR;
        }

        /* add each additional parameter to the local varstore */
        while (htmlMarkup->attrib != NULL)
        {
            /* No need to duplicate values, as varstore is
             * destroyed before this function returns.  
             */
            if(StoreVariable(&varstore, htmlMarkup->attrib->name,
                htmlMarkup->attrib->value, VAR_TYPE_SET_MACRO, VAR_FLAG_NONE,
                NULL, NULL) == FALSE)
            {
                HtpMsg(MSG_ERROR, task->infile, "unable to add variable to block's local context");
                DestroyVariableStore(&varstore);
                DestroyAttribute(nameAttrib);
                FreeMemory(nameAttrib);
                return MARKUP_ERROR;
            }
            htmlMarkup->attrib = htmlMarkup->attrib->next;
        }

        /* make this variable store the topmost context */
        PushVariableStoreContext(task->varstore, &varstore);
        topVarstore = &varstore;
    }
    else
    {
        topVarstore = task->varstore;
    }

    if (type == VAR_TYPE_SET_MACRO)
    {
        /* get the value of the macro */
        value = GetVariableValue(task->varstore, name);

        /* if NULL, then the macro was declared with no value, this is okay, */
        /* just don't do anything */
        if(value == NULL)
        {
            DestroyAttribute(nameAttrib);
            FreeMemory(nameAttrib);
            return DISCARD_MARKUP;
        }

        HtpMsg(MSG_INFO, task->infile, "dereferencing macro \"%s\"", name);

        PutStreamString(task->outfile, value);
        DestroyAttribute(nameAttrib);
        FreeMemory(nameAttrib);
        return DISCARD_MARKUP;
    }
    else /* type == VAR_TYPE_BLOCK_MACRO */
    {
        /* !! magic number */
        TASK newTask;
        STREAM *blockFile;

        /* get the blockFile of the macro */
        blockFile = (STREAM*) GetVariableValue(task->varstore, name);

        /* if NULL, big-time error */
        assert (blockFile != NULL);

        HtpMsg(MSG_INFO, task->infile, "dereferencing block macro \"%s\"%s", name, noexpand ? " with noexpand" : "");
        if (noexpand)
        {
            PutStreamString(task->outfile, blockFile->u.buffer.buffer);
            DestroyAttribute(nameAttrib);
            FreeMemory(nameAttrib);
            return DISCARD_MARKUP;
        }

        result = CreateBufferReader(&incfile, blockFile);
        assert(result);

        /* build a new task structure */
        newTask.infile = &incfile;
        newTask.outfile = task->outfile;
        newTask.sourceFilename = task->sourceFilename;

        /* re-use current variable store if no local variable store was */
        /* allocated, otherwise use the new one */
        newTask.varstore = topVarstore;

        /* process the new input file */
        result = ProcessTask(&newTask);
        if (!result) {
            /* Error message was already spitted out.  However, we
             * should give a hint where the meta-tag was called.  
             */
            HtpMsg(MSG_ERROR, task->infile, "... in block macro \"%s\"", name);
        }

        /* remove the new context (and make sure it is, in fact, the block's */
        /* context) */
        if(topVarstore == &varstore)
        {
            assert(varstore.child == NULL);
            PopVariableStoreContext(&varstore);
            DestroyVariableStore(&varstore);
        }

        CloseStream(&incfile);

        /* if the new file did not process, return an error, otherwise discard */
        /* the markup */
        DestroyAttribute(nameAttrib);
        FreeMemory(nameAttrib);
        return (result == TRUE) ? DISCARD_MARKUP : MARKUP_ERROR;
    }
}   


