/**************************************************************
 * 
 * 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.
 * 
 *************************************************************/

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#if (defined(_WIN32) || defined(_MSDOS) || defined(__IBMC__))
#include <io.h>
#else
#include <unistd.h>
#endif
#include "cpp.h"
/*
 * lexical FSM encoding
 *   when in state state, and one of the characters
 *   in ch arrives, enter nextstate.
 *   States >= S_SELF are either final, or at least require special action.
 *   In 'fsm' there is a line for each state X charset X nextstate.
 *   List chars that overwrite previous entries later (e.g. C_ALPH
 *   can be overridden by '_' by a later entry; and C_XX is the
 *   the universal set, and should always be first.
 *   States above S_SELF are represented in the big table as negative values.
 *   S_SELF and S_SELFB encode the resulting token type in the upper bits.
 *   These actions differ in that S_SELF doesn't have a lookahead char,
 *   S_SELFB does.
 *
 *   The encoding is blown out into a big table for time-efficiency.
 *   Entries have
 *      nextstate: 6 bits; ?\ marker: 1 bit; tokentype: 9 bits.
 */

#define	MAXSTATE		32
#define	ACT(tok,act)	((tok<<7)+act)
#define	QBSBIT			0100
#define	GETACT(st)		((st>>7)&0x1ff)

/* character classes */
#define	C_WS	1
#define	C_ALPH	2
#define	C_NUM	3
#define	C_EOF	4
#define	C_XX	5

enum state
{
    START = 0, NUM1, NUM2, NUM3, ID1, ST1, ST2, ST3, COM1, COM2, COM3, COM4,
    CC1, CC2, WS1, PLUS1, MINUS1, STAR1, SLASH1, PCT1, SHARP1,
    CIRC1, GT1, GT2, LT1, LT2, OR1, AND1, ASG1, NOT1, DOTS1,
    S_SELF = MAXSTATE, S_SELFB, S_EOF, S_NL, S_EOFSTR,
    S_STNL, S_COMNL, S_EOFCOM, S_COMMENT, S_EOB, S_WS, S_NAME
};

int tottok;
int tokkind[256];
struct fsm
{
    int state;                          /* if in this state */
    uchar ch[4];                        /* and see one of these characters */
    int nextstate;                      /* enter this state if +ve */
};

 /*const*/ struct fsm fsm[] = {
    /* start state */
		 {START, {C_XX}, ACT(UNCLASS, S_SELF)},
		 {START, {' ', '\t', '\v'}, WS1},
		 {START, {C_NUM}, NUM1},
		 {START, {'.'}, NUM3},
		 {START, {C_ALPH}, ID1},
		 {START, {'L'}, ST1},
		 {START, {'"'}, ST2},
		 {START, {'\''}, CC1},
		 {START, {'/'}, COM1},
		 {START, {EOFC}, S_EOF},
		 {START, {'\n'}, S_NL},
		 {START, {'-'}, MINUS1},
		 {START, {'+'}, PLUS1},
		 {START, {'<'}, LT1},
		 {START, {'>'}, GT1},
		 {START, {'='}, ASG1},
		 {START, {'!'}, NOT1},
		 {START, {'&'}, AND1},
		 {START, {'|'}, OR1},
		 {START, {'#'}, SHARP1},
		 {START, {'%'}, PCT1},
		 {START, {'['}, ACT(SBRA, S_SELF)},
		 {START, {']'}, ACT(SKET, S_SELF)},
		 {START, {'('}, ACT(LP, S_SELF)},
		 {START, {')'}, ACT(RP, S_SELF)},
		 {START, {'*'}, STAR1},
		 {START, {','}, ACT(COMMA, S_SELF)},
		 {START, {'?'}, ACT(QUEST, S_SELF)},
		 {START, {':'}, ACT(COLON, S_SELF)},
		 {START, {';'}, ACT(SEMIC, S_SELF)},
		 {START, {'{'}, ACT(CBRA, S_SELF)},
		 {START, {'}'}, ACT(CKET, S_SELF)},
		 {START, {'~'}, ACT(TILDE, S_SELF)},
		 {START, {'^'}, CIRC1},

    /* saw a digit */
		 {NUM1, {C_XX}, ACT(NUMBER, S_SELFB)},
		 {NUM1, {C_NUM, C_ALPH, '.'}, NUM1},
		 {NUM1, {'E', 'e'}, NUM2},
		 {NUM1, {'_'}, ACT(NUMBER, S_SELFB)},

    /* saw possible start of exponent, digits-e */
		 {NUM2, {C_XX}, ACT(NUMBER, S_SELFB)},
		 {NUM2, {'+', '-'}, NUM1},
		 {NUM2, {C_NUM, C_ALPH}, NUM1},
		 {NUM2, {'_'}, ACT(NUMBER, S_SELFB)},

    /* saw a '.', which could be a number or an operator */
		 {NUM3, {C_XX}, ACT(DOT, S_SELFB)},
		 {NUM3, {'.'}, DOTS1},
		 {NUM3, {C_NUM}, NUM1},

		 {DOTS1, {C_XX}, ACT(UNCLASS, S_SELFB)},
		 {DOTS1, {C_NUM}, NUM1},
		 {DOTS1, {'.'}, ACT(ELLIPS, S_SELF)},

    /* saw a letter or _ */
		 {ID1, {C_XX}, ACT(NAME, S_NAME)},
		 {ID1, {C_ALPH, C_NUM}, ID1},

    /* saw L (start of wide string?) */
		 {ST1, {C_XX}, ACT(NAME, S_NAME)},
		 {ST1, {C_ALPH, C_NUM}, ID1},
		 {ST1, {'"'}, ST2},
		 {ST1, {'\''}, CC1},

    /* saw " beginning string */
		 {ST2, {C_XX}, ST2},
		 {ST2, {'"'}, ACT(STRING, S_SELF)},
		 {ST2, {'\\'}, ST3},
		 {ST2, {'\n'}, S_STNL},
		 {ST2, {EOFC}, S_EOFSTR},

    /* saw \ in string */
		 {ST3, {C_XX}, ST2},
		 {ST3, {'\n'}, S_STNL},
		 {ST3, {EOFC}, S_EOFSTR},

    /* saw ' beginning character const */
		 {CC1, {C_XX}, CC1},
		 {CC1, {'\''}, ACT(CCON, S_SELF)},
		 {CC1, {'\\'}, CC2},
		 {CC1, {'\n'}, S_STNL},
		 {CC1, {EOFC}, S_EOFSTR},

    /* saw \ in ccon */
		 {CC2, {C_XX}, CC1},
		 {CC2, {'\n'}, S_STNL},
		 {CC2, {EOFC}, S_EOFSTR},

    /* saw /, perhaps start of comment */
		 {COM1, {C_XX}, ACT(SLASH, S_SELFB)},
		 {COM1, {'='}, ACT(ASSLASH, S_SELF)},
		 {COM1, {'*'}, COM2},
		 {COM1, {'/'}, COM4},

    /* saw / followed by *, start of comment */
		 {COM2, {C_XX}, COM2},
		 {COM2, {'\n'}, S_COMNL},
		 {COM2, {'*'}, COM3},
		 {COM2, {EOFC}, S_EOFCOM},

    /* saw the * possibly ending a comment */
		 {COM3, {C_XX}, COM2},
		 {COM3, {'\n'}, S_COMNL},
		 {COM3, {'*'}, COM3},
		 {COM3, {'/'}, S_COMMENT},

    /* // comment */
		 {COM4, {C_XX}, COM4},
		 {COM4, {'\n'}, S_NL},
		 {COM4, {EOFC}, S_EOFCOM},

    /* saw white space, eat it up */
		 {WS1, {C_XX}, S_WS},
		 {WS1, {'\t', '\v', ' '}, WS1},

    /* saw -, check --, -=, -> */
		 {MINUS1, {C_XX}, ACT(MINUS, S_SELFB)},
		 {MINUS1, {'-'}, ACT(MMINUS, S_SELF)},
		 {MINUS1, {'='}, ACT(ASMINUS, S_SELF)},
		 {MINUS1, {'>'}, ACT(ARROW, S_SELF)},

    /* saw +, check ++, += */
		 {PLUS1, {C_XX}, ACT(PLUS, S_SELFB)},
		 {PLUS1, {'+'}, ACT(PPLUS, S_SELF)},
		 {PLUS1, {'='}, ACT(ASPLUS, S_SELF)},

    /* saw <, check <<, <<=, <= */
		 {LT1, {C_XX}, ACT(LT, S_SELFB)},
		 {LT1, {'<'}, LT2},
		 {LT1, {'='}, ACT(LEQ, S_SELF)},
		 {LT2, {C_XX}, ACT(LSH, S_SELFB)},
		 {LT2, {'='}, ACT(ASLSH, S_SELF)},

    /* saw >, check >>, >>=, >= */
		 {GT1, {C_XX}, ACT(GT, S_SELFB)},
		 {GT1, {'>'}, GT2},
		 {GT1, {'='}, ACT(GEQ, S_SELF)},
		 {GT2, {C_XX}, ACT(RSH, S_SELFB)},
		 {GT2, {'='}, ACT(ASRSH, S_SELF)},

    /* = */
		 {ASG1, {C_XX}, ACT(ASGN, S_SELFB)},
		 {ASG1, {'='}, ACT(EQ, S_SELF)},

    /* ! */
		 {NOT1, {C_XX}, ACT(NOT, S_SELFB)},
		 {NOT1, {'='}, ACT(NEQ, S_SELF)},

    /* & */
		 {AND1, {C_XX}, ACT(AND, S_SELFB)},
		 {AND1, {'&'}, ACT(LAND, S_SELF)},
		 {AND1, {'='}, ACT(ASAND, S_SELF)},

    /* | */
		 {OR1, {C_XX}, ACT(OR, S_SELFB)},
		 {OR1, {'|'}, ACT(LOR, S_SELF)},
		 {OR1, {'='}, ACT(ASOR, S_SELF)},

    /* # */
		 {SHARP1, {C_XX}, ACT(SHARP, S_SELFB)},
		 {SHARP1, {'#'}, ACT(DSHARP, S_SELF)},

    /* % */
		 {PCT1, {C_XX}, ACT(PCT, S_SELFB)},
		 {PCT1, {'='}, ACT(ASPCT, S_SELF)},

    /* * */
		 {STAR1, {C_XX}, ACT(STAR, S_SELFB)},
		 {STAR1, {'='}, ACT(ASSTAR, S_SELF)},

    /* ^ */
		 {CIRC1, {C_XX}, ACT(CIRC, S_SELFB)},
		 {CIRC1, {'='}, ACT(ASCIRC, S_SELF)},

		 {-1, "", 0}
};

/* first index is char, second is state */
/* increase #states to power of 2 to encourage use of shift */
short bigfsm[256][MAXSTATE];

void
    expandlex(void)
{
     /* const */ struct fsm *fp;
    int i, j, nstate;

    for (fp = fsm; fp->state >= 0; fp++)
    {
        for (i = 0; fp->ch[i]; i++)
        {
            nstate = fp->nextstate;
            if (nstate >= S_SELF)
                nstate = ~nstate;
            switch (fp->ch[i])
            {

                case C_XX:              /* random characters */
                    for (j = 0; j < 256; j++)
                        bigfsm[j][fp->state] = (short) nstate;
                    continue;
                case C_ALPH:
                    for (j = 0; j < 256; j++)
#ifdef S390
						if( isalpha( j ) || (j == '_') )
#else							
                        if (('a' <= j && j <= 'z') || ('A' <= j && j <= 'Z')
                            || j == '_')
#endif
                            bigfsm[j][fp->state] = (short) nstate;
                    continue;
                case C_NUM:
                    for (j = '0'; j <= '9'; j++)
                        bigfsm[j][fp->state] = (short) nstate;
                    continue;
                default:
                    bigfsm[fp->ch[i]][fp->state] = (short) nstate;
            }
        }
    }

    /*
     * install special cases for ? (trigraphs),  \ (splicing), runes, and
     * EOB
     */
    for (i = 0; i < MAXSTATE; i++)
    {
        for (j = 0; j < 0xFF; j++)
            if (j == '?' || j == '\\' || j == '\n' || j == '\r')
            {
                if (bigfsm[j][i] > 0)
                    bigfsm[j][i] = ~bigfsm[j][i];
                bigfsm[j][i] &= ~QBSBIT;
            }
        bigfsm[EOB][i] = ~S_EOB;
        if (bigfsm[EOFC][i] >= 0)
            bigfsm[EOFC][i] = ~S_EOF;
    }
}

void
    fixlex(void)
{
    /* do C++ comments? */
    if ((Cplusplus == 0) || (Cflag != 0))
        bigfsm['/'][COM1] = bigfsm['x'][COM1];
}

/*
 * fill in a row of tokens from input, terminated by NL or END
 * First token is put at trp->lp.
 * Reset is non-zero when the input buffer can be "rewound."
 * The value is a flag indicating that possible macros have
 * been seen in the row.
 */
int
    gettokens(Tokenrow * trp, int reset)
{
    register int c, state, oldstate;
    register uchar *ip;
    register Token *tp, *maxp;
    int runelen;
    Source *s = cursource;
    int nmac = 0;

    tp = trp->lp;
    ip = s->inp;
    if (reset)
    {
        s->lineinc = 0;
        if (ip >= s->inl)
        {                               /* nothing in buffer */
            s->inl = s->inb;
            fillbuf(s);
            ip = s->inp = s->inb;
        }
        else
            if (ip >= s->inb + (3 * INS / 4))
            {
                memmove(s->inb, ip, 4 + s->inl - ip);
                s->inl = s->inb + (s->inl - ip);
                ip = s->inp = s->inb;
            }
    }
    maxp = &trp->bp[trp->max];
    runelen = 1;
    for (;;)
    {
continue2:
        if (tp >= maxp)
        {
            trp->lp = tp;
            tp = growtokenrow(trp);
            maxp = &trp->bp[trp->max];
        }
        tp->type = UNCLASS;
        tp->t = ip;
        tp->wslen = 0;
        tp->flag = 0;
        state = START;
        for (;;)
        {
            oldstate = state;

            c = *ip;
						
            if ((state = bigfsm[c][state]) >= 0)
            {
                ip += runelen;
                runelen = 1;
                continue;
            }
            state = ~state;
    reswitch:
            switch (state & 0177)
            {
                case S_SELF:
                    ip += runelen;
                    runelen = 1;
                case S_SELFB:
                    tp->type = (unsigned char) GETACT(state);
                    tp->len = ip - tp->t;
                    tp++;
                    goto continue2;

                case S_NAME:            /* like S_SELFB but with nmac check */
                    tp->type = NAME;
                    tp->len = ip - tp->t;
                    nmac |= quicklook(tp->t[0], tp->len > 1 ? tp->t[1] : 0);
                    tp++;
                    goto continue2;

                case S_WS:
                    tp->wslen = ip - tp->t;
                    tp->t = ip;
                    state = START;
                    continue;

                default:
                    if ((state & QBSBIT) == 0)
                    {
                        ip += runelen;
                        runelen = 1;
                        continue;
                    }
                    state &= ~QBSBIT;
                    s->inp = ip;

					if (c == '\n')
					{
					    while (s->inp + 1 >= s->inl && fillbuf(s) != EOF);
					
						if (s->inp[1] == '\r')
						{
							memmove(s->inp + 1, s->inp + 2, s->inl - s->inp + 2);
							s->inl -= 1;
						}

                        goto reswitch;
					}

					if (c == '\r')
					{
    				    while (s->inp + 1 >= s->inl && fillbuf(s) != EOF);
					
						if (s->inp[1] == '\n')
						{
							memmove(s->inp, s->inp + 1, s->inl - s->inp + 1);
							s->inl -= 1;
						}
						else
							*s->inp = '\n';

						state = oldstate;
                        continue;
					}

                    if (c == '?')
                    {                   /* check trigraph */
                        if (trigraph(s))
                        {
                            state = oldstate;
                            continue;
                        }
                        goto reswitch;
                    }
                    if (c == '\\')
                    {                   /* line-folding */
                        if (foldline(s))
                        {
                            s->lineinc++;
                            state = oldstate;
                            continue;
                        }
                        goto reswitch;
                    }
                    error(WARNING, "Lexical botch in cpp");
                    ip += runelen;
                    runelen = 1;
                    continue;

                case S_EOB:
                    s->inp = ip;
                    fillbuf(cursource);
                    state = oldstate;
                    continue;

                case S_EOF:
                    tp->type = END;
                    tp->len = 0;
                    s->inp = ip;
                    if (tp != trp->bp && (tp - 1)->type != NL && cursource->fd != -1)
                        error(WARNING, "No newline at end of file");
                    trp->lp = tp + 1;
                    return nmac;

                case S_STNL:
                    error(ERROR, "Unterminated string or char const");
                case S_NL:
                    tp->t = ip;
                    tp->type = NL;
                    tp->len = 1;
                    tp->wslen = 0;
                    s->lineinc++;
                    s->inp = ip + 1;
                    trp->lp = tp + 1;
                    return nmac;

                case S_EOFSTR:
                    error(FATAL, "EOF in string or char constant");
                    break;

                case S_COMNL:
                    s->lineinc++;
                    state = COM2;
                    ip += runelen;
                    runelen = 1;
                    continue;

                case S_EOFCOM:
                    error(WARNING, "EOF inside comment");
                    --ip;
                case S_COMMENT:
					if (!Cflag)
					{
						tp->t = ++ip;
						tp->t[-1] = ' ';
						tp->wslen = 1;
						state = START;
						continue;
					}
					else
					{
	                    runelen = 1;
                        s->lineinc = 0;;
                        tp->type = COMMENT;
						tp->flag |= XTWS;
					}
            }
            break;
        }
        ip += runelen;
        runelen = 1;
        tp->len = ip - tp->t;
        tp++;
    }
}

/* have seen ?; handle the trigraph it starts (if any) else 0 */
int
    trigraph(Source * s)
{
    uchar c;

    while (s->inp + 2 >= s->inl && fillbuf(s) != EOF);
	;
    if (s->inp[1] != '?')
        return 0;
    c = 0;
    switch (s->inp[2])
    {
        case '=':
            c = '#';
            break;
        case '(':
            c = '[';
            break;
        case '/':
            c = '\\';
            break;
        case ')':
            c = ']';
            break;
        case '\'':
            c = '^';
            break;
        case '<':
            c = '{';
            break;
        case '!':
            c = '|';
            break;
        case '>':
            c = '}';
            break;
        case '-':
            c = '~';
            break;
    }
    if (c)
    {
        *s->inp = c;
        memmove(s->inp + 1, s->inp + 3, s->inl - s->inp + 2);
        s->inl -= 2;
    }
    return c;
}

int
    foldline(Source * s)
{
    int n = 1;

	/* skip pending wihite spaces */
	while ((s->inp[n] == ' ') || (s->inp[n] == '\t'))
	{
		n++;
	    if ((s->inp + n >= s->inl) && (fillbuf(s) == EOF))
			break;
	}

	/* refill buffer */
    while (s->inp + (n + 1) >= s->inl && fillbuf(s) != EOF);

    /* skip DOS line ends */
    if (((s->inp[n] == '\r') && (s->inp[n+1] == '\n')) ||
		((s->inp[n] == '\n') && (s->inp[n+1] == '\r')))
        n++;

    if ((s->inp[n] == '\n') || (s->inp[n] == '\r'))
    {
        memmove(s->inp, s->inp + n + 1, s->inl - s->inp + n + 2);
        s->inl -= n + 1;
        return 1;
    }
    return 0;
}

int
    fillbuf(Source * s)
{
    int n;

    if (s->fd < 0 || (n = read(s->fd, (char *) s->inl, INS / 8)) <= 0)
        n = 0;
    s->inl += n;
    s->inl[0] = s->inl[1] = s->inl[2] = s->inl[3] = EOB;
    if (n == 0)
    {
        s->inl[0] = s->inl[1] = s->inl[2] = s->inl[3] = EOFC;
        return EOF;
    }
    return 0;
}

/*
 * Push down to new source of characters.
 * If fd>0 and str==NULL, then from a file `name';
 * if fd==-1 and str, then from the string.
 */
Source *
    setsource(char *name, int path, int fd, char *str, int wrap)
{
    Source *s = new(Source);
    int len;

    s->line = 1;
    s->lineinc = 0;
    s->fd = fd;
    s->filename = name;
    s->next = cursource;
    s->ifdepth = 0;
    s->pathdepth = path;
	s->wrap = wrap;

    cursource = s;

	if (s->wrap)
		genwrap(0);

    /* slop at right for EOB */
    if (str)
    {
        len = strlen(str);
        s->inb = domalloc(len + 4);
        s->inp = s->inb;
        strncpy((char *) s->inp, str, len);
    }
    else
    {
        s->inb = domalloc(INS + 4);
        s->inp = s->inb;
        len = 0;
    }
    s->inl = s->inp + len;
    s->inl[0] = s->inl[1] = EOB;

    return s;
}

void
    unsetsource(void)
{
    Source *s = cursource;

	if (s->wrap)
		genwrap(1);

    if (s->fd >= 0)
    {
        close(s->fd);
        dofree(s->inb);
    }
    cursource = s->next;
    dofree(s);
}
