/*
**      expr.c      - Conditional expression evaluation module
**
**
** Copyright (c) 1995  Hughes Technologies Pty Ltd
**
** Permission to use, copy, and distribute for non-commercial purposes,
** is hereby granted without fee, providing that the above copyright
** notice appear in all copies and that both the copyright notice and this
** permission notice appear in supporting documentation.
**
** This software is provided "as is" without any expressed or implied warranty.
**
**
*/

#include <stdio.h>
#include <sys/types.h>
#include <ctype.h>
#include <msql.h>

#ifdef OS2
#  include <common/config.h>
#  include <common/portable.h>
#  include "w3-msql.h"
#  include "w3_yacc.h"
#else
#  include "portable.h"
#  include "w3-msql.h"
#  include "y_tab.h"
#endif



typedef struct val {
        char    *tokVal;
        int     intVal,
                type;
        struct  val *next;
} val_t;

typedef struct cond {
        int     res;
        struct  cond *next;
} cond_t;



static val_t    *head = NULL;
static cond_t   *condHead = NULL;

static  int     breakout;


startIf()
{
        breakout = 0;
        head = NULL;
}



startExpr()
{
        head = NULL;
}




addValue(val,type)
        char    *val;
        int     type;
{
        char    *cp,
                *varVal,
                buf[160];
        val_t   *new;

        /*
        ** If we've done enough to know the end result just bail out
        */
        if (breakout)
                return;

        /*
        ** Push this value onto the stack
        */
        new = (val_t *)malloc(sizeof(val_t));
        new->tokVal = val;
        new->type = type;
        new->next = 0;

        /*
        ** Do anything type specific
        */

        if (new->type == TEXT)
        {
                new->tokVal = (char *)strdup(val+1);
                *(new->tokVal + strlen(val) - 2)  = 0;
        }

        if (new->type == VAR)
        {
                varVal = (char *)getVariable(val);
                if (varVal == NULL)
                {
                        sprintf(buf,"Unknown variable '%s'",val);
                        error(buf);
                        exit(1);
                }
                new->tokVal = (char *)strdup(varVal);
                new->type = TEXT;
        }


        /*
        ** Can we look on this value as a numeric if we have to ?
        */
        cp = new->tokVal;
        while(*cp)
        {
                if (!isdigit(*cp))
                {
                        break;
                }
                cp++;
        }
        if (*cp == 0 && *new->tokVal != 0)
        {
                new->type = NUM;
                new->intVal = atoi(new->tokVal);
        }
        new->next = head;
        head = new;
}





addRowValue(handle,off)
        char    *handle,
                *off;
{
        char    *val,
                *cp;
        int     offset = atoi(off);
        qHandle *qh;
        val_t   *new;

        qh = getHandle(handle);
        if (offset > msqlNumFields(qh->res))
        {
                error("Row value offset too large for query data");
                exit(1);
        }
        if(!qh->row)
        {
                val = "";
        }
        else
        {
                val = (char *)qh->row[offset];
        }

        /*
        ** Push this value onto the stack
        */
        new = (val_t *)malloc(sizeof(val_t));
        new->tokVal = (char *)strdup(val);
        new->type = TEXT;
        new->next = 0;

        /*
        ** Can we look on this value as a numeric if we have to ?
        */
        cp = new->tokVal;
        while(*cp)
        {
                if (!isdigit(*cp))
                {
                        break;
                }
                cp++;
        }
        if (*cp == 0 && *new->tokVal != 0)
        {
                new->type = NUM;
                new->intVal = atoi(new->tokVal);
        }
        new->next = head;
        head = new;
}






int doComparison(op)
        int     op;
{
        val_t   *val1,
                *val2;
        int     res;

        if (breakout)
                return;

        /*
        ** Pop two values to work on
        */
        if (!head)
        {
                error("Malformed condition");
                exit(1);
        }
        if (!head->next)
        {
                error("Malformed condition");
                exit(1);
        }
        val2 = head;
        val1 = val2->next;
        head = val1;

        /*
        ** Do the comparison
        */
        if (val1->type != val2->type)
        {
                error("Bad types in condition");
                exit(1);
        }
        switch(op)
        {
                case EQ:
                        if (val1->type == NUM)
                        {
                                res = (val1->intVal == val2->intVal);
                        }
                        else
                        {
                                res = (strcmp(val1->tokVal,val2->tokVal)==0);
                        }
                        break;

                case NE:
                        if (val1->type == NUM)
                        {
                                res = (val1->intVal != val2->intVal);
                        }
                        else
                        {
                                res = (strcmp(val1->tokVal,val2->tokVal)!=0);
                        }
                        break;

                case GT:
                        if (val1->type == NUM)
                        {
                                res = (val1->intVal > val2->intVal);
                        }
                        else
                        {
                                return(-1);
                        }
                        break;

                case LT:
                        if (val1->type == NUM)
                        {
                                res = (val1->intVal < val2->intVal);
                        }
                        else
                        {
                                return(-1);
                        }
                        break;

                case GE:
                        if (val1->type == NUM)
                        {
                                res = (val1->intVal >= val2->intVal);
                        }
                        else
                        {
                                return(-1);
                        }
                        break;

                case LE:
                        if (val1->type == NUM)
                        {
                                res = (val1->intVal <= val2->intVal);
                        }
                        else
                        {
                                return(-1);
                        }
                        break;
        }
        if (val2->tokVal)
        {
                free(val2->tokVal);
        }
        free(val2);
        if (val1->tokVal)
        {
                free(val1->tokVal);
        }
        val1->intVal = res;
        val1->type = NUM;
        return(0);
}




doLogical(op)
        int     op;
{
        val_t   *val1,
                *val2;
        int     res;

        if (breakout)
                return;

        /*
        ** Pop two values to work on
        */
        if (!head)
        {
                error("Malformed condition");
                exit(1);
        }
        if (!head->next)
        {
                error("Malformed condition");
                exit(1);
        }
        val2 = head;
        val1 = val2->next;
        head = val1;

        /*
        ** Do the comparison
        */
        if (val1->type != val2->type)
        {
                return(-1);
        }

        if (op == LOGICAL_AND)
        {
                res = val1->intVal && val2->intVal;
        }
        else
        {
                res = val1->intVal || val2->intVal;
        }
        if (val2->tokVal)
        {
                free(val2->tokVal);
        }
        free(val2);
        if (val1->tokVal)
        {
                free(val1->tokVal);
        }
        val1->intVal = res;
        val1->type = NUM;
        return(0);
}


int endIfCond()
{
        val_t   *cur;
        int     res = -1;
        cond_t  *new;

        if (head)
        {
                res = head->intVal;
                cur = head->next;
                while(head)
                {
                        if (head->type == TEXT)
                        {
                                free(head->tokVal);
                                free(head);
                        }
                        head = cur;
                        if (cur)
                                cur = cur->next;
                }
        }

        new = (cond_t *)malloc(sizeof(cond_t));
        new->res = res;
        new->next = condHead;
        condHead = new;
        head = NULL;
}



doElse()
{
        if (!condHead)
        {
                error("Bad ELSE location");
                exit(1);
        }
        if ( condHead->res != -1)
                condHead->res = ! condHead->res;
}


endIf()
{
        cond_t  *cur;

        if (!condHead)
        {
                error("Bad FI location");
                exit(1);
        }
        cur = condHead->next;
        free(condHead);
        condHead = cur;
}


int checkIf()
{
        if (!condHead)
        {
                return(1);
        }
        if (condHead->res == 1)
                return(1);
        return(0);
}



int doMath(op)
        int     op;
{
        val_t   *val1,
                *val2;
        int     res;

        if (breakout)
                return;

        /*
        ** Pop two values to work on
        */
        if (!head)
        {
                error("Malformed expression");
                exit(1);
        }
        if (!head->next)
        {
                error("Malformed expression");
                exit(1);
        }
        val2 = head;
        val1 = val2->next;
        head = val1;

        /*
        ** Do the maths
        */
        if (val1->type != val2->type)
        {
                error("Types mismatch");
                exit(1);
        }
        if (val1->type != NUM)
        {
                error("Non-numeric value in expression");
                exit(1);
        }
        switch(op)
        {
                case ADD:
                        res = (val1->intVal + val2->intVal);
                        break;

                case SUB:
                        res = (val1->intVal - val2->intVal);
                        break;

                case MUL:
                        res = (val1->intVal * val2->intVal);
                        break;

                case DIV:
                        res = (val1->intVal / val2->intVal);
                        break;
        }
        if (val2->tokVal)
        {
                free(val2->tokVal);
        }
        free(val2);
        if (val1->tokVal)
        {
                free(val1->tokVal);
        }
        val1->intVal = res;
        val1->type = NUM;
        return(0);
}



doSet(var)
        char    *var;
{
        char    buf[80],
                *val;

        switch(head->type)
        {
                case NUM:
                        sprintf(buf,"%d",head->intVal);
                        setVariable(var,buf);
                        break;

                case TEXT:
                        setVariable(var,head->tokVal);
                        break;

                case VAR:
                        val = (char *)getVariable(head->tokVal);
                        setVariable(var,val);
                        break;
        }
        free(head);
        head = NULL;
}



doSetDefault(var)
        char    *var;
{
        char    buf[80],
                *val;

        switch(head->type)
        {
                case NUM:
                        sprintf(buf,"%d",head->intVal);
                        setVariableDefault(var,buf);
                        break;

                case TEXT:
                        setVariableDefault(var,head->tokVal);
                        break;

                case VAR:
                        val = (char *)getVariable(head->tokVal);
                        setVariableDefault(var,val);
                        break;
        }
        free(head);
        head = NULL;
}

