/* 
XLIB Librarian
Copyright 1997, 1998 Free Software Foundation, Inc.
Written by Ian Lance Taylor, Cygnus Support.
Copyright 2006 David Lindauer.

This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.

This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
GNU General Public License for more details.

You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA

You may contact the author of this derivative at:
	mailto::camille@bluegrass.net
 */
/*
 * 68K/386 32-bit C compiler.
 *
 * copyright (c) 1997, David Lindauer
 * 
 * This compiler is intended for educational use.  It may not be used
 * for profit without the express written consent of the author.
 *
 * It may be freely redistributed, as long as this notice remains intact
 * and either the original sources or derived sources 
 * are distributed along with any executables derived from the originals.
 *
 * The author is not responsible for any damages that may arise from use
 * of this software, either idirect or consequential.
 *
 * V1.85 July 2000
 * David Lindauer, camille@bluegrass.net
 *
 * Credits to Mathew Brandt for original K&R C compiler
 *
 */
/*
 * This module is the phi-text interface for the compiler.  Files are
 * orignilly read in as shorts to accomdate phi-text.
 */
#include <stdio.h>
#include <string.h>
#include <memory.h>
#include <ctype.h>
#include "utype.h"
#include "interp.h"

extern BYTE startchars[];
extern BYTE symchars[];
extern BYTE whitespacechars[];
extern BYTE commentchars[];

PHITEXT phiparms = 
{
    4, 0, 0x0f, 0, 0, 0, 0x7f
};
BOOL phiused = FALSE;
BOOL putback = FALSE;

int currentphifg = 0;

static PHITEXT phidefaults = 
{
    4, 0, 0x0f, 0, 0, 0, 0x7f
};
static int pbchar = 0;

int repeat = 0;

void PhiInit(void)
{
    phiparms = phidefaults;
    putback = FALSE;
    phiused = FALSE;
    currentphifg = phidefaults.fgc;
}

//-------------------------------------------------------------------------

void BadString(void)
{
    fatal("Bad escape sequence in string");
}

/* Get a char from file */
static BOOL phichar(BYTE *ch, FILE *file)
{
    while (TRUE)
    {
        *ch = fgetc(file);
        if (feof(file))
            return (FALSE);
        if (*ch == 0x1f)
        {
            phiused = TRUE;
            continue;
        }
        break;
    }
    return (TRUE);
}

/*
 * Get a line from the file.  At this point the line is still streamed
 * phi-text
 */
BOOL philine(BYTE *buf, int len, FILE *file)
{
    if (len < 2 || !phichar(buf, file))
    {
        *buf = 0;
        return (FALSE);
    }
    while (len > 2)
    {
        if (*buf == 0x0a || (phiused && (*buf == 0x14 ||  *buf == 0x15 ||  *buf
            == 0x16)))
            break;
        if (!phichar(++buf, file))
            break;
        len--;
    }
    *buf++ = 0x0a;
    *buf++ = 0;
    return (TRUE);
}

/*
 * Parse the phi-text stream and get the next character.  Attributes are
 * stripped at this stage so all we get is the bank and char info
 */
short parsechar(BYTE **buf)
{
    short rv;
    if (putback)
    {
        (*buf)++;
        putback = FALSE;
        return (pbchar);
    }
    if (!phiused)
        return (*(*buf)++);
    while (TRUE)
    {
        if (repeat)
        {
            repeat--;
            rv = phiparms.cwb + (phiparms.bank << 7);
            break;
        }
        rv = *(*buf)++;
        if (rv &0x80)
        {
            char grp = rv &0x70;
            switch (grp)
            {
                case 0:
                    {
                        int repeatlevel = 0;
                        (*buf)--;
                        while (((rv =  * * buf) &0xf0) == 0x80)
                        {
                            (*buf)++;
                            repeat += ((rv &0x0f) << (repeatlevel++ *4)) + 1;
                        }
                        repeat++;
                    }
                    break;
                case 0x10:
                    phiparms.bank = rv &0x0f;
                    break;
                case 0x20:
                case 0x30:
                    phiparms.attrib = rv &0x1f;
                    break;
                case 0x40:
                    phiparms.fgc = rv &0x0f;
                    break;
                case 0x50:
                    phiparms.bgc = rv &0x0f;
                    break;
                case 0x60:
                    phiparms.style = rv &0x0f;
                    break;
                case 0x70:
                    phiparms.size = rv &0x0f;
                    break;
            }
        }
        else
        {
            if (rv)
                phiparms.cwb = rv;
            if (rv &0x60)
            {
                if (phiparms.attrib &HIDDEN || phiparms.style)
                    continue;
                rv = rv + (phiparms.bank << 7);
            }
            else
                if (rv == 0x0a)
                    phiparms = phidefaults;
            break;
        }
    }
    return (rv);
}

/*
 * In case we have to step back one char
 */
void putphiback(short ch)
{
    putback = TRUE;
    pbchar = ch;
}

/* The next few routines check for specific symbol types, sort of analogous
 * to the ctype library
 */
BOOL isstartchar(short val)
{
    int bit = 1 << (7-(val &0x07));
    int byte = val >> 3;
    return (startchars[byte] &bit);
}

//-------------------------------------------------------------------------

BOOL issymchar(short val)
{
    int bit = 1 << (7-(val &0x07));
    int byte = val >> 3;
    return (symchars[byte] &bit);
}

//-------------------------------------------------------------------------

BOOL iswhitespacechar(short val)
{
    int bit = 1 << (7-(val &0x07));
    int byte = val >> 3;
    return (whitespacechars[byte] &bit);
}

//-------------------------------------------------------------------------

BOOL iscommentchar(short val)
{
    int bit = 1 << (7-(val &0x07));
    int byte = val >> 3;
    return (commentchars[byte] &bit);
}

/* These reads in a char for a C style string lit */
static BYTE *getstringchar(BYTE *rv, BYTE *bufptr)
{
    putback = FALSE;
    if (phiused)
    {
        if (repeat)
        {
            repeat--;
            *rv = phiparms.cwb;
            return (bufptr);
        }
        if ((*bufptr > 0x7f) || phiparms.bank)
        {
            *rv =  *bufptr;
            if (*rv &0x80)
            {
                char grp =  *rv &0x70;
                switch (grp)
                {
                    case 0:
                        break;
                    case 0x10:
                        phiparms.bank =  *rv &0x0f;
                        break;
                    case 0x20:
                    case 0x30:
                        phiparms.attrib =  *rv &0x1f;
                        break;
                    case 0x40:
                        phiparms.fgc =  *rv &0x0f;
                        break;
                    case 0x50:
                        phiparms.bgc =  *rv &0x0f;
                        break;
                    case 0x60:
                        phiparms.style =  *rv &0x0f;
                        break;
                    case 0x70:
                        phiparms.size =  *rv &0x0f;
                        break;
                }
            }
            return (++bufptr);
        }
    }

    if (*bufptr == '\\')
    {
        bufptr++;
        if (isdigit(*bufptr) && (*bufptr < '8'))
        {
            unsigned temp = 0;
            while (isdigit(*bufptr) &&  *bufptr < '8')
                temp = (temp << 3) + (*bufptr++ - '0');
            if (temp > 255)
                BadString();

            *rv = (BYTE)temp;
        }
        else
        switch (*bufptr++)
        {
            case 'b':
                *rv = 8;
                break;
            case 'f':
                *rv = 12;
                break;
            case 'n':
                *rv = 10;
                break;
            case 'r':
                *rv = 13;
                break;
            case 't':
                *rv = 9;
                break;
            case '\'':
                *rv = '\'';
                break;
            case '"':
                *rv = '"';
                break;
            case '\\':
                *rv = '\\';
                break;
            case 'x':
                {
                    unsigned temp = 0;
                    while (isxdigit(*bufptr))
                    {
                        temp = (temp << 4) + (*bufptr - '0');
                        if (*bufptr++ > '9')
                            temp -= 7;
                    }
                    if (temp > 255)
                        BadString();
                    *rv = (BYTE)temp;
                }
                break;
            default:
                BadString();
        }
    }
    else
        *rv =  *bufptr++;
    return (bufptr);
}

/* This reads in a string lit */
short getphistring(BYTE *obuf, BYTE **ibuf, short endchar)
{
    BYTE rv;
    if (phiused)
    {
        if (phidefaults.size != phiparms.size)
            *obuf++ = 0xf0 | phiparms.size;
        if (phidefaults.style != phiparms.style)
            *obuf++ = 0xe0 | phiparms.style;
        if (phidefaults.bgc != phiparms.bgc)
            *obuf++ = 0xd0 | phiparms.bgc;
        if (phidefaults.fgc != phiparms.fgc)
            *obuf++ = 0xc0 | phiparms.fgc;
        if (phidefaults.attrib != phiparms.attrib)
            *obuf++ = 0xa0 | phiparms.attrib;
        if (repeat)
        {
            BadString();
            repeat = 0;
            return (endchar);
        }
    }
    *ibuf = getstringchar(&rv,  *ibuf);
    while (rv && ((rv + (phiparms.bank << 7)) != endchar))
    {
        if (phiused && (rv < 0x20))
        {
            if ((rv == 0x0a))
            {
                while (((BYTE)*(obuf - 1)) &0x80)
                    obuf--;
            }
        }
        *obuf++ = rv;
        if (phiused && (rv == 0x0a))
        {
            if (phidefaults.size != phiparms.size)
                *obuf++ = 0xf0 | phiparms.size;
            if (phidefaults.style != phiparms.style)
                *obuf++ = 0xe0 | phiparms.style;
            if (phidefaults.bgc != phiparms.bgc)
                *obuf++ = 0xd0 | phiparms.bgc;
            if (phidefaults.fgc != phiparms.fgc)
                *obuf++ = 0xc0 | phiparms.fgc;
            if (phidefaults.attrib != phiparms.attrib)
                *obuf++ = 0xa0 | phiparms.attrib;
        }
        *ibuf = getstringchar(&rv,  *ibuf);
    }
    *obuf = 0;
    phiparms.cwb = rv;
    return (rv + (phiparms.bank << 7));
}

/* This is for reading in phi-stream literal strings
 */
long getphichar(BYTE **ibuf)
{
    BYTE rv;
    *ibuf = getstringchar(&rv,  *ibuf);
    if (!phiused)
        return (rv);
    while (rv &0x80)
        *ibuf = getstringchar(&rv,  *ibuf);
    if (!rv)
        return (0);
    phiparms.cwb = rv;
    return (((long)phiparms.size << 28) + ((long)phiparms.style << 24) + ((long)
        phiparms.fgc << 20) + ((long)phiparms.bgc << 16) + ((long)
        phiparms.attrib << 11) + ((long)phiparms.bank << 7) + (rv &0x7f));
}

/* The next one writes a color index to a streamed file */
void phifg(int color, FILE *file)
{
    currentphifg = color;
    if (phiused)
        fputc(color | 0xc0, file);
}

/* Now conver to upper case */
short phiupper(short val)
{
    if (val > 0x60 && val < 0x7b)
        return val &~0x20;
    return (val);
}

/* Convert the short stream back to streamed one char at a time */
int installphichar(short curchar, BYTE *buf, int i)
{
    short rv = 0;
    int bank = curchar >> 7;
    int cwb = curchar &0x7f;
    buf = buf + i;
    if (!phiused)
    {
        *buf = curchar;
        return (1);
    }
    if (i)
    {
        buf--;
        if (((*buf) &0xf0) == 0x90)
        if ((*buf &0x0f) != bank)
        {
            *buf++ = 0x90 | bank;
        }
        else
            rv--;
        else
        {
            buf++;
            if (bank)
            {
                *buf++ = 0x90 | bank;
                rv++;
            }
        }
    }
    else
    if (bank)
    {
        *buf++ = 0x90 | bank;
        rv++;
    }
    *buf++ = cwb;
    rv++;
    if (bank)
    {
        *buf++ = 0x90 | bank;
        rv++;
    }
    return (rv);
}

/* Convert a stream to flats */
short phistreamtoflat(BYTE *out, BYTE *in, int size, BOOL useparms)
{
    int count = 0, fcount;
    long rv;
    PHITEXT oparms = phiparms;
    PHITEXT lastphiparms = phiparms;
    int orepeat = repeat;

    if (!phiused)
    {
        count = strlen(in);
        strcpy(out, in);
        return (count);
    }
    repeat = 0;
    while (rv = parsechar(&in))
    {
        if ((rv > 0x1f) && useparms)
            rv |= ((long)phiparms.attrib << 11) + ((long)phiparms.fgc << 16) + 
                ((long)phiparms.bgc << 20) + ((long)phiparms.style << 24) + (
                (long)phiparms.size << 28);
        if (rv == 0x0a || rv == 0x14 || rv == 0x15 || rv == 0x16)
            phiparms = lastphiparms;
        if (size > 2)
        {
            out[count++] = rv >> 24;
            out[count++] = (rv >> 16) &0xff;
        }
        if (size > 1)
            out[count++] = (rv >> 8) &0xff;
        out[count++] = (rv) &0xff;
        lastphiparms = phiparms;
    }
    fcount = count;
    out[count++] = 0;
    if (size > 1)
        out[count++] = 0;
    if (size > 2)
    {
        out[count++] = 0;
        out[count++] = 0;
    }

    repeat = orepeat;
    phiparms = oparms;
    return (fcount);
}

/* Compare two phi-text streams */
int phicmp(BYTE *str1, BYTE *str2)
{
    BYTE buf1[400], buf2[400],  *p1 = buf1,  *p2 = buf2;
    int count1, count2;
    if (!phiused)
        return (strcmp(str1, str2));

    count1 = phistreamtoflat(buf1, str1, FALSE, FALSE);
    count2 = phistreamtoflat(buf2, str2, FALSE, FALSE);

    while (count1 && count2)
    {
        int val1 = ((*p1++) << 8) +  *p1++;
        int val2 = ((*p2++) << 8) +  *p2++;
        if (val1 > val2)
            return (1);
        if (val1 < val2)
            return ( - 1);
        count1--;
        count2--;
    }
    if (count1 == count2)
        return (0);
    if (count1)
        return (1);
    return (2);
}
