/**************************************************************
 * 
 * Licensed to the Apache Software Foundation (ASF) under one
 * or more contributor license agreements.  See the NOTICE file
 * distributed with this work for additional information
 * regarding copyright ownership.  The ASF licenses this file
 * to you under the Apache License, Version 2.0 (the
 * "License"); you may not use this file except in compliance
 * with the License.  You may obtain a copy of the License at
 * 
 *   http://www.apache.org/licenses/LICENSE-2.0
 * 
 * Unless required by applicable law or agreed to in writing,
 * software distributed under the License is distributed on an
 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
 * KIND, either express or implied.  See the License for the
 * specific language governing permissions and limitations
 * under the License.
 * 
 *************************************************************/

#ifdef _MSC_VER
#	define _POSIX_
#endif
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#ifdef __hpux
#	define _HPUX_SOURCE
#endif
#if defined(__IBMC__) || defined(__EMX__)
#   define PATH_MAX _MAX_PATH
#endif
#include <limits.h>

#include "cpp.h"

#define NCONCAT 16384

/*
 * do a macro definition.  tp points to the name being defined in the line
 */
void
    dodefine(Tokenrow * trp)
{
    Token *tp;
    Nlist *np;
    Source *s;
    Tokenrow *def, *args;
	static uchar location[(PATH_MAX + 8) * NINC], *cp;

    tp = trp->tp + 1;
    if (tp >= trp->lp || tp->type != NAME)
    {
        error(ERROR, "#defined token is not a name");
        return;
    }
    np = lookup(tp, 1);
    if (np->flag & ISUNCHANGE)
    {
        error(ERROR, "#defined token %t can't be redefined", tp);
        return;
    }
    /* collect arguments */
    tp += 1;
    args = NULL;
    if (tp < trp->lp && tp->type == LP && tp->wslen == 0)
    {
        /* macro with args */
        int narg = 0;

        tp += 1;
        args = new(Tokenrow);
        maketokenrow(2, args);
        if (tp->type != RP)
        {
            int err = 0;

            for (;;)
            {
                Token *atp;

                if (tp->type != NAME)
                {
                    err++;
                    break;
                }
                if (narg >= args->max)
                    growtokenrow(args);
                for (atp = args->bp; atp < args->lp; atp++)
                    if (atp->len == tp->len
                        && strncmp((char *) atp->t, (char *) tp->t, tp->len) == 0)
                        error(ERROR, "Duplicate macro argument");
                *args->lp++ = *tp;
                narg++;
                tp += 1;
                if (tp->type == RP)
                    break;
                if (tp->type != COMMA)
                {
                    err++;
                    break;
                }
                tp += 1;
            }
            if (err)
            {
                error(ERROR, "Syntax error in macro parameters");
                return;
            }
        }
        tp += 1;
    }
    trp->tp = tp;
    if (((trp->lp) - 1)->type == NL)
        trp->lp -= 1;
    def = normtokenrow(trp);
    if (np->flag & ISDEFINED)
    {
        if (comparetokens(def, np->vp)
            || (np->ap == NULL) != (args == NULL)
            || (np->ap && comparetokens(args, np->ap)))
		{
			if ( np->loc )
            	error(ERROR,
					"Macro redefinition of %t (already defined at %s)",
					trp->bp + 2, np->loc);
			else
            	error(ERROR,
					"Macro redefinition of %t (already defined at %s)",
					trp->bp + 2, "commandline" );
		}
    }
    if (args)
    {
        Tokenrow *tap;

        tap = normtokenrow(args);
        dofree(args->bp);
        args = tap;
    }
    np->ap = args;
    np->vp = def;
    np->flag |= ISDEFINED;

	/* build location string of macro definition */
    for (cp = location, s = cursource; s; s = s->next)
        if (*s->filename)
		{
			if (cp != location)
				*cp++ = ' ';
            sprintf((char *)cp, "%s:%d", s->filename, s->line);
			cp += strlen((char *)cp);
		}

	np->loc = newstring(location, strlen((char *)location), 0);

	if (Mflag)
	{
		if (np->ap)
			error(INFO, "Macro definition of %s(%r) [%r]", np->name, np->ap, np->vp);
		else
			error(INFO, "Macro definition of %s [%r]", np->name, np->vp);
	}
}

/*
 * Definition received via -D or -U
 */
void
    doadefine(Tokenrow * trp, int type)
{
    Nlist *np;
	static uchar onestr[2] = "1";
    static Token onetoken[1] = {{NUMBER, 0, 0, 1, onestr, 0}};
    static Tokenrow onetr = {onetoken, onetoken, onetoken + 1, 1};

    trp->tp = trp->bp;
    if (type == 'U')
    {
        if (trp->lp - trp->tp != 2 || trp->tp->type != NAME)
            goto syntax;
        if ((np = lookup(trp->tp, 0)) == NULL)
            return;
        np->flag &= ~ISDEFINED;
        return;
    }

    if (type == 'A')
    {
        if (trp->tp >= trp->lp || trp->tp->type != NAME)
            goto syntax;
        trp->tp->type = ARCHITECTURE;
        np = lookup(trp->tp, 1);
        np->flag |= ISARCHITECTURE;
        trp->tp += 1;
        if (trp->tp >= trp->lp || trp->tp->type == END)
        {
            np->vp = &onetr;
            return;
        }
        else
            error(FATAL, "Illegal -A argument %r", trp);
    }

    if (trp->tp >= trp->lp || trp->tp->type != NAME)
        goto syntax;
    np = lookup(trp->tp, 1);
    np->flag |= ISDEFINED;
    trp->tp += 1;
    if (trp->tp >= trp->lp || trp->tp->type == END)
    {
        np->vp = &onetr;
        return;
	}
	if (trp->tp->type != ASGN)
		goto syntax;
	trp->tp += 1;
	if ((trp->lp - 1)->type == END)
		trp->lp -= 1;
	np->vp = normtokenrow(trp);
	return;
syntax:
	error(FATAL, "Illegal -D or -U argument %r", trp);
}



/*
 * Do macro expansion in a row of tokens.
 * Flag is NULL if more input can be gathered.
 */
void
	expandrow(Tokenrow * trp, char *flag)
{
	Token *	tp;
	Nlist *	np;

	MacroValidatorList 	validators;
	mvl_init(&validators);
    /* Sets all token-identifiers to 0 because tokens may not be initialised (never use C!) */
	tokenrow_zeroTokenIdentifiers(trp);

	if (flag)
		setsource(flag, -1, -1, "", 0);
	for (tp = trp->tp; tp < trp->lp;)
	{
		mvl_check(&validators, tp);

		if (tp->type != NAME
			|| quicklook(tp->t[0], tp->len > 1 ? tp->t[1] : 0) == 0
			|| (np = lookup(tp, 0)) == NULL
			|| (np->flag & (ISDEFINED | ISMAC)) == 0
			|| (np->flag & ISACTIVE) != 0)
		{
			tp++;
			continue;
		}
		trp->tp = tp;
		if (np->val == KDEFINED)
		{
			tp->type = DEFINED;
			if ((tp + 1) < trp->lp && (tp + 1)->type == NAME)
				(tp + 1)->type = NAME1;
			else
				if ((tp + 3) < trp->lp && (tp + 1)->type == LP
					&& (tp + 2)->type == NAME && (tp + 3)->type == RP)
					(tp + 2)->type = NAME1;
				else
					error(ERROR, "Incorrect syntax for `defined'");
			tp++;
			continue;
		}
		else
			if (np->val == KMACHINE)
			{
				if (((tp - 1) >= trp->bp) && ((tp - 1)->type == SHARP))
				{
					tp->type = ARCHITECTURE;
					if ((tp + 1) < trp->lp && (tp + 1)->type == NAME)
						(tp + 1)->type = NAME2;
					else
						if ((tp + 3) < trp->lp && (tp + 1)->type == LP
							&& (tp + 2)->type == NAME && (tp + 3)->type == RP)
							(tp + 2)->type = NAME2;
						else
							error(ERROR, "Incorrect syntax for `#machine'");
				}
				tp++;
				continue;
			}

		if (np->flag & ISMAC)
			builtin(trp, np->val);
		else
			expand(trp, np, &validators);
		tp = trp->tp;
	}	// end for
	if (flag)
		unsetsource();

	mvl_destruct(&validators);
}

/*
 * Expand the macro whose name is np, at token trp->tp, in the tokenrow.
 * Return trp->tp at the first token next to be expanded
 * (ordinarily the beginning of the expansion)
 * I.e.: the same position as before!
 * Only one expansion is performed, then we return to the expandrow()
 * loop and start at same position.
 */
void
	expand(Tokenrow * trp, Nlist * np, MacroValidatorList *	pValidators)
{
//	Token * pOldNextTp;
	Tokenrow ntr;
	int ntokc, narg, i;
	Tokenrow *atr[NARG + 1];

	if (Mflag == 2)
	{
		if (np->ap)
			error(INFO, "Macro expansion of %t with %s(%r)", trp->tp, np->name, np->ap);
		else
			error(INFO, "Macro expansion of %t with %s", trp->tp, np->name);
	}

	copytokenrow(&ntr, np->vp);         /* copy macro value */
	if (np->ap == NULL)                 /* parameterless */
		ntokc = 1;
	else
	{
		ntokc = gatherargs(trp, atr, &narg);
		if (narg < 0)
		{                               /* not actually a call (no '(') */
			trp->tp++;
			return;
		}
		if (narg != rowlen(np->ap))
		{
			error(ERROR, "Disagreement in number of macro arguments");
			trp->tp += ntokc;
			return;
		}

		/** If gatherargs passed a macro validating token, this token
			must become valid here.
			trp->tp+0 was checked in expandrow(), so we dont need to do it
			again here:
		*/
		for (i = 1; i < ntokc; i++)
		{
			mvl_check(pValidators,trp->tp+i);
		}

		substargs(np, &ntr, atr);		/* put args into replacement */
		for (i = 0; i < narg; i++)
		{
			dofree(atr[i]->bp);
			dofree(atr[i]);
		}
	}

/* old
	np->flag |= ISACTIVE;
*/

/* rh
*/
	doconcat(&ntr);                     /* execute ## operators */
	ntr.tp = ntr.bp;
	makespace(&ntr, trp->tp);

/* old
//	expandrow(&ntr, "<expand>");
//	insertrow(trp, ntokc, &ntr);
//	dofree(ntr.bp);
//	np->flag &= ~ISACTIVE;
*/

/* NP
		// Replace macro by its value:
*/
//	pOldNextTp = trp->tp+ntokc;
	tokenrow_zeroTokenIdentifiers(&ntr);
	insertrow(trp, ntokc, &ntr);
		/* Reassign old macro validators:
		*/
//	mvl_move(pValidators, trp->tp - pOldNextTp);

		/* add validator for just invalidated macro:
		*/
	np->flag |= ISACTIVE;
	if (trp->tp != trp->lp)
	{  	/* tp is a valid pointer: */
		mvl_add(pValidators,np,trp->tp);
	}
	else
	{	/* tp is == lp, therefore does not point to valid memory: */
		mvl_add(pValidators,np,0);
	}
		/* reset trp->tp to original position:
		*/
	trp->tp -= ntr.lp - ntr.bp;			/* so the result will be tested for macros from the same position again */

	dofree(ntr.bp);

	return;
}

/*
 * Gather an arglist, starting in trp with tp pointing at the macro name.
 * Return total number of tokens passed, stash number of args found.
 * trp->tp is not changed relative to the tokenrow.
 */
int
	gatherargs(Tokenrow * trp, Tokenrow ** atr, int *narg)
{
	int parens = 1;
	int ntok = 0;
	Token *bp, *lp;
	Tokenrow ttr;
	int ntokp;
	int needspace;

	*narg = -1;                         /* means that there is no macro
										 * call */
	/* look for the ( */
	for (;;)
	{
		trp->tp++;
		ntok++;
		if (trp->tp >= trp->lp)
		{
			gettokens(trp, 0);
			if ((trp->lp - 1)->type == END)
			{
				trp->lp -= 1;
				trp->tp -= ntok;
				return ntok;
			}
		}
		if (trp->tp->type == LP)
			break;
		if (trp->tp->type != NL)
			return ntok;
	}
	*narg = 0;
	ntok++;
	ntokp = ntok;
	trp->tp++;
	/* search for the terminating ), possibly extending the row */
	needspace = 0;
	while (parens > 0)
	{
		if (trp->tp >= trp->lp)
			gettokens(trp, 0);
		if (needspace)
		{
			needspace = 0;
			/* makespace(trp); [rh] */
		}
		if (trp->tp->type == END)
		{
			trp->lp -= 1;
			trp->tp -= ntok;
			error(ERROR, "EOF in macro arglist");
			return ntok;
		}
		if (trp->tp->type == NL)
		{
			trp->tp += 1;
			adjustrow(trp, -1);
			trp->tp -= 1;
			/* makespace(trp); [rh] */
			needspace = 1;
			continue;
		}
		if (trp->tp->type == LP)
			parens++;
		else
			if (trp->tp->type == RP)
				parens--;
		trp->tp++;
		ntok++;
	}
	trp->tp -= ntok;
	/* Now trp->tp won't move underneath us */
	lp = bp = trp->tp + ntokp;
	for (; parens >= 0; lp++)
	{
		if (lp->type == LP)
		{
            parens++;
            continue;
        }
        if (lp->type == RP)
            parens--;
        if (lp->type == DSHARP)
			lp->type = DSHARP1;         /* ## not special in arg */
        if ((lp->type == COMMA && parens == 0) ||
				( parens < 0 && ((lp - 1)->type != LP)))
        {
            if (*narg >= NARG - 1)
				error(FATAL, "Sorry, too many macro arguments");
            ttr.bp = ttr.tp = bp;
            ttr.lp = lp;
            atr[(*narg)++] = normtokenrow(&ttr);
            bp = lp + 1;
        }
    }
    return ntok;
}

/*
 * substitute the argument list into the replacement string
 *  This would be simple except for ## and #
 */
void
	substargs(Nlist * np, Tokenrow * rtr, Tokenrow ** atr)
{
	Tokenrow tatr;
	Token *tp;
	int ntok, argno;

	for (rtr->tp = rtr->bp; rtr->tp < rtr->lp;)
	{
		if (rtr->tp->type == SHARP)
		{                               /* string operator */
			tp = rtr->tp;
			rtr->tp += 1;
			if ((argno = lookuparg(np, rtr->tp)) < 0)
			{
				error(ERROR, "# not followed by macro parameter");
				continue;
			}
			ntok = 1 + (rtr->tp - tp);
			rtr->tp = tp;
			insertrow(rtr, ntok, stringify(atr[argno]));
			continue;
		}
		if (rtr->tp->type == NAME
			&& (argno = lookuparg(np, rtr->tp)) >= 0)
		{
			if (((rtr->tp + 1) < rtr->lp && (rtr->tp + 1)->type == DSHARP)
				|| (rtr->tp != rtr->bp  && (rtr->tp - 1)->type == DSHARP))
			{
				copytokenrow(&tatr, atr[argno]);
				makespace(&tatr, rtr->tp);
				insertrow(rtr, 1, &tatr);
				dofree(tatr.bp);
			}
            else
            {
                copytokenrow(&tatr, atr[argno]);
				makespace(&tatr, rtr->tp);
                expandrow(&tatr, "<macro>");
                insertrow(rtr, 1, &tatr);
                dofree(tatr.bp);
            }
            continue;
        }
        rtr->tp++;
    }
}

/*
 * Evaluate the ## operators in a tokenrow
 */
void
    doconcat(Tokenrow * trp)
{
    Token *ltp, *ntp;
    Tokenrow ntr;
    int len;

    for (trp->tp = trp->bp; trp->tp < trp->lp; trp->tp++)
    {
        if (trp->tp->type == DSHARP1)
            trp->tp->type = DSHARP;
        else
            if (trp->tp->type == DSHARP)
            {
				int  i;
                char tt[NCONCAT];

                ltp = trp->tp - 1;
				ntp = trp->tp + 1;

				if (ltp < trp->bp || ntp >= trp->lp)
                {
                    error(ERROR, "## occurs at border of replacement");
                    continue;
				}

				ntp = ltp;
				i   = 1;
				len = 0;

				do
				{
					if (len + ntp->len + ntp->wslen > sizeof(tt))
					{
						error(ERROR, "## string concatination buffer overrun");
						break;
					}

					if (ntp != trp->tp + 1)
					{
						strncpy((char *) tt + len, (char *) ntp->t - ntp->wslen,
							    ntp->len + ntp->wslen);
						len += ntp->len + ntp->wslen;
					}
					else	// Leerzeichen um ## herum entfernen:
					{
						strncpy((char *) tt + len, (char *) ntp->t, ntp->len);
						len += ntp->len;
					}
					
					ntp = trp->tp + i;
					i++;						
				}
				while (ntp < trp->lp);

                tt[len] = '\0';
				setsource("<##>", -1, -1, tt, 0);
                maketokenrow(3, &ntr);
                gettokens(&ntr, 1);
                unsetsource();
				if (ntr.bp->type == UNCLASS)
					error(WARNING, "Bad token %r produced by ##", &ntr);
				while ((ntr.lp-1)->len == 0 && ntr.lp != ntr.bp)
					ntr.lp--;

			    doconcat(&ntr);
                trp->tp = ltp;
				makespace(&ntr, ltp);
                insertrow(trp, ntp - ltp, &ntr);
                dofree(ntr.bp);
                trp->tp--;
            }
    }
}

/*
 * tp is a potential parameter name of macro mac;
 * look it up in mac's arglist, and if found, return the
 * corresponding index in the argname array.  Return -1 if not found.
 */
int
    lookuparg(Nlist * mac, Token * tp)
{
    Token *ap;

    if (tp->type != NAME || mac->ap == NULL)
        return -1;
    for (ap = mac->ap->bp; ap < mac->ap->lp; ap++)
    {
        if (ap->len == tp->len && strncmp((char *) ap->t, (char *) tp->t, ap->len) == 0)
            return ap - mac->ap->bp;
    }
    return -1;
}

/*
 * Return a quoted version of the tokenrow (from # arg)
 */
#define	STRLEN	512
Tokenrow *
    stringify(Tokenrow * vp)
{
    static Token t = {STRING, 0, 0, 0, NULL, 0};
	static Tokenrow tr = {&t, &t, &t + 1, 1};
    Token *tp;
    uchar s[STRLEN];
    uchar *sp = s, *cp;
    int i, instring;

    *sp++ = '"';
    for (tp = vp->bp; tp < vp->lp; tp++)
    {
        instring = tp->type == STRING || tp->type == CCON;
        if (sp + 2 * tp->len + tp->wslen  >= &s[STRLEN - 10])
        {
            error(ERROR, "Stringified macro arg is too long");
            break;
        }

        // Change by np 31.10.2001, #93725 - begin
        if ( tp->wslen > 0 )
        *sp++ = ' ';
        // change end.

        for (i = 0, cp = tp->t; (unsigned int)i < tp->len; i++)
        {
            if (instring && (*cp == '"' || *cp == '\\'))
                *sp++ = '\\';
            *sp++ = *cp++;
        }
    }
    *sp++ = '"';
    *sp = '\0';
	sp = s;
    t.len = strlen((char *) sp);
    t.t = newstring(sp, t.len, 0);
    return &tr;
}

/*
 * expand a builtin name
 */
void
    builtin(Tokenrow * trp, int biname)
{
    char *op;
    Token *tp;
    Source *s;

    tp = trp->tp;
    trp->tp++;
    /* need to find the real source */
    s = cursource;
    while (s && s->fd == -1)
        s = s->next;
    if (s == NULL)
        s = cursource;
	/* most are strings */
    tp->type = STRING;
    if (tp->wslen)
    {
        *outptr++ = ' ';
        tp->wslen = 1;
    }
    op = outptr;
    *op++ = '"';
    switch (biname)
    {

        case KLINENO:
            tp->type = NUMBER;
            op = outnum(op - 1, s->line);
            break;

        case KFILE:
            {
                char *src = s->filename;

                while ((*op++ = *src++) != 0)
                    if (src[-1] == '\\')
                        *op++ = '\\';
                op--;
                break;
            }

        case KDATE:
            strncpy(op, curtime + 4, 7);
            strncpy(op + 7, curtime + 20, 4);
            op += 11;
            break;

        case KTIME:
            strncpy(op, curtime + 11, 8);
            op += 8;
            break;

        default:
            error(ERROR, "cpp botch: unknown internal macro");
            return;
    }
    if (tp->type == STRING)
        *op++ = '"';
    tp->t = (uchar *) outptr;
    tp->len = op - outptr;
    outptr = op;
}

