/*
 *  ChkTeX v1.4, resource file reader.
 *  Copyright (C) 1995-96 Jens T. Berger Thielemann
 *
 *  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., 675 Mass Ave, Cambridge, MA 02139, USA.
 *
 *  Contact the author at:
 *              Jens Berger
 *              Spektrumvn. 4
 *              N-0666 Oslo
 *              Norway
 *              E-mail: <jensthi@ifi.uio.no>
 *
 *
 */

#include "ChkTeX.h"

struct WordList
        Silent          = {{0L}},
        Abbrev          = {{0L}},
        SlowAbbrev      = {{0L}},
        Linker          = {{0L}},
        IJAccent        = {{0L}},
        Italic          = {{0L}},
        ItalCmd         = {{0L}},
        UserWarn        = {{0L}},
        CmdLine         = {{0L}},
        PostLink        = {{0L}},
        WipeArg         = {{0L}},
        VerbEnvir       = {{0L}},
        MathEnvir       = {{0L}},
        MathRoman       = {{0L}},
        TeXInputs       = {{0L}},
        HyphDash        = {{0L}},
        NumDash         = {{0L}},
        WordDash        = {{0L}},
        CenterDots      = {{0L}},
        LowDots         = {{0L}},
        OutFormat       = {{0L}};

struct KeyWord
{
    STRPTR  Name;
    STRPTR  *String;          /* Either of these are set */
    struct  WordList
            *List;
};


struct KeyWord Keys [] =
{
    {"ABBREV",      NULL,       &Abbrev},
    {"CENTERDOTS",  NULL,       &CenterDots},
    {"CMDLINE",     NULL,       &CmdLine},
    {"HYPHDASH",    NULL,       &HyphDash},
    {"IJACCENT",    NULL,       &IJAccent},
    {"ITALIC",      NULL,       &Italic},
    {"LINKER",      NULL,       &Linker},
    {"LOWDOTS",     NULL,       &LowDots},
    {"MATHENVIR",   NULL,       &MathEnvir},
    {"MATHROMAN",   NULL,       &MathRoman},
    {"NUMDASH",     NULL,       &NumDash},
    {"OUTFORMAT",   NULL,       &OutFormat},
    {"POSTLINK",    NULL,       &PostLink},
    {"SILENT",      NULL,       &Silent},
    {"TEXINPUTS",   NULL,       &TeXInputs},
    {"USERWARN",    NULL,       &UserWarn},
    {"VERBENVIR",   NULL,       &VerbEnvir},
    {"WIPEARG",     NULL,       &WipeArg},
    {"WORDDASH",    NULL,       &WordDash},
    
    {"VERBCLEAR",   &VerbClear, NULL},
    {NULL,          NULL,       NULL}
};


/***************************** RESOURCE HANDLING **************************/

enum Token
{
    tkEof   = 0x01,     /* End-of-file */
    tkOpen  = 0x02,     /* { */
    tkClose = 0x04,     /* } */
    tkEqual = 0x08,     /* = */
    tkWord  = 0x10,     /* Keyword */
    tkItem  = 0x20      /* List item */
};

static enum Token Expect;

enum Token ReadWord        __PROTO((STRPTR, FILE *));

static ULONG RsrcLine;


/*
 * Parses the contents of the `.chktexrc' file.
 *
 * Either:
 *
 *  Keyword { word1 word2 ... }
 *
 * Or:
 *
 *  Keyword = word1
 *
 */

BOOL ReadRC(const STRPTR Filename)
{
    STRPTR      String = "";
    BOOL        Success = FALSE;
    FILE        *fh;
    enum Token  Token;
    ULONG       Counter;

    struct WordList
        *CurList = NULL;      /* Where we'll put the words we find */
    STRPTR
        *CurStr = NULL;

    /* Interpret incoming words as ... */
    enum
    {
        whList,     /* List elements */
        whSolo,     /* Solo elements */
        whKey       /* Keywords */
    } What = whKey;

    CurList = NULL;
    RsrcLine = 0;
    Expect = tkWord | tkEof;

    if((fh = fopen(Filename, "r")))
    {
        do
        {
            Token = ReadWord(ReadBuffer, fh);
            ifn(Expect & Token)
            {
                switch(Token)
                {
                case tkItem:
                    String = "item";
                    break;
                case tkWord:
                    String = "word";
                    break;
                case tkEqual:
                    String = "`='";
                    break;
                case tkOpen:
                    String = "`{'";
                    break;
                case tkClose:
                    String = "`}'";
                    break;
                case tkEof:
                    String = "EOF";
                    break;
                }
                PrintPrgErr(pmFaultFmt, Filename, RsrcLine, String);
                Token = tkEof;
            }

            switch(Token)
            {
            case tkWord:
                for(Counter = 0;
                    Keys[Counter].Name;
                    Counter++)
                {
                    if(!strcasecmp(ReadBuffer, Keys[Counter].Name))
                    {
                        if(Keys[Counter].List)
                        {
                            Expect = tkOpen;
                            CurList = Keys[Counter].List;
                        }
                        else
                        {
                            Expect = tkEqual;
                            CurStr = Keys[Counter].String;
                        }
                        break;
                    }
                }
                if(!Keys[Counter].Name)
                    PrintPrgErr(pmKeyWord, ReadBuffer, Filename);
                break;
            case tkItem:
                switch(What)
                {
                case whSolo:
                    ifn(*CurStr = strdup(ReadBuffer))
                        PrintPrgErr(pmStrDupErr);

                    What = whKey;
                    Expect = tkWord | tkEof;
                    break;
                case whList:
                    InsertWord(ReadBuffer, CurList);
		    break;
		case whKey:
		    PrintPrgErr(pmAssert);
                }
                break;
            case tkEqual:
                What = whSolo;
                Expect = tkItem;
                break;
            case tkOpen:
                Expect = tkItem | tkClose;
                What = whList;
                break;
            case tkClose:
                Expect = tkWord | tkEof;
                What = whKey;
                break;
	    case tkEof:
		break;
            }
        } while(Token != tkEof);

        fclose(fh);
    }

    return(Success);
}

/*
 * Reads a token from the `.chktexrc' file. Matches brackets. Uses
 * ReadLine().
 */

enum Token ReadWord(STRPTR Buffer, FILE *fh)
{
    static
        STRPTR  String = NULL;
    static
        UBYTE   StatBuf[BUFSIZ];
    enum Token Retval = tkEof;

    UWORD       Chr;

    STRPTR      Ptr = NULL;
    BOOL        OnceMore = TRUE, Cont = TRUE;

    if(Buffer)
    {
        do
        {
            if(!(String && *String))
            {
                if(fgets(StatBuf, BUFSIZ-1, fh))
                    String = strip(StatBuf, STRP_RGT);
                RsrcLine++;
            }

            Ptr = Buffer;
            if((String = strip(String, STRP_LFT)))
            {
                switch(Chr = *String)
                {
                case 0:
                case CMNT:
                    String = NULL;
                    break;

                case QUOTE:     /* Quoted argument */
                    Cont = TRUE;
                    String++;

                    while(Cont)
                    {
                        switch(Chr = *String++)
                        {
                        case 0:
                        case QUOTE:
                            Cont = FALSE;
                            break;
                        case ESCAPE:
                            ifn(Chr = MapChars(&String))
                                break;

                            /* FALLTHRU */
                        default:
                            *Ptr++ = Chr;
                        }
                    }
                    *Ptr = 0;
                    Retval = tkItem;
                    OnceMore = FALSE;
                    break;
                case '{':
                case '}':
                case '=':

#define IFTOK(c, ctxt, tk) if((Chr == c) && (Expect & (ctxt))) \
                           { Retval = tk; break; }

                    OnceMore = FALSE;
                    String++;

                    IFTOK('{', tkOpen | tkEqual, tkOpen)
                    else IFTOK('=', tkOpen | tkEqual, tkEqual)
                    else IFTOK('}', tkClose, tkClose)

                    String--;

                    /* FALLTHRU */
                default:        /* Non-quoted argument */
                    OnceMore = FALSE;
                    if(Expect & tkWord)
                    {
                        while(Cont)
                        {
                            Chr = *String++;
                            if(isalpha(Chr))
                                *Ptr++ = Chr;
                            else
                                Cont = FALSE;
                        }
                        String--;
                        Retval = tkWord;
                    }
                    else
                    {
                        while(Cont)
                        {
                            switch(Chr = *String++)
                            {
                            case CMNT:
                            case 0:
                                String = NULL;
                                Cont = FALSE;
                                break;
                            case ESCAPE:
                                ifn(Chr = MapChars(&String))
                                    break;

                                *Ptr++ = Chr;
                                break;
                            default:
                                if(!isspace(Chr))
                                    *Ptr++ = Chr;
                                else
                                    Cont = FALSE;
                            }
                        }
                        Retval = tkItem;
                    }

                    ifn(Buffer[0])
                    {
                        PrintPrgErr(pmEmptyToken);
                        if(*String)
                            String++;
                    }
                    *Ptr = 0;
                    break;
                }
            }
            else
                OnceMore = FALSE;
        } while(OnceMore);
    }
    return(Retval);
}



/*
 * Translates escape codes. Give it a pointer to the char after the
 * escape char, and we'll return what it maps to.
 */

#define MAP(a,b)        case a: Tmp = b; break;

UBYTE MapChars(STRPTR *String)
{
    UBYTE       Chr, Tmp = 0;
    UWORD       Cnt;

    Chr = *((STRPTR) (*String)++);

    switch(tolower(Chr))
    {
        MAP(QUOTE, QUOTE);
        MAP(ESCAPE, ESCAPE);
        MAP(CMNT, CMNT);
        MAP('n', '\n');
        MAP('r', '\r');
        MAP('b', '\b');
        MAP('t', '\t');
        MAP('f', '\f');
        MAP('{', '{');
        MAP('}', '}');
        MAP('=', '=');
        MAP(' ', ' ');
    case 'x':
        Tmp = 0;
        
        for(Cnt = 0; Cnt < 2; Cnt++)
        {
            if(isxdigit(Chr = *((*String)++)))
            {
                Chr = toupper(Chr);
                Tmp = (Tmp<<4) + Chr;
                
                if(isdigit(Chr))
                    Tmp -= '0';
                else
                                        Tmp -= 'A' - 10;
            }
            else
            {
                if(Chr) {
                    PrintPrgErr(pmNotPSDigit, Chr, "hex");
                    Tmp = 0;
                }
                break;
            }
        }
        break;
    case '0': case '1': case '2': case '3': case '4':
    case '5': case '6': case '7':
        
        Tmp = Chr - '0';
        
        for(Cnt = 0; Cnt < 2; Cnt++)
        {
            Chr = *((*String)++);
            if(within('0',Chr,'7'))
                Tmp = (Tmp * 8) + Chr - '0';
            else
            {
                if(Chr)
                {
                    PrintPrgErr(pmNotPSDigit, Chr, "octal");
                    Tmp = 0;
                }
                break;
            }
        }
        break;
    case 'd':
        for(Cnt = 0; Cnt < 3; Cnt++)
        {
            if(isdigit(Chr = *((*String)++)))
                Tmp = (Tmp * 10) + Chr - '0';
            else
            {
                if(Chr)
                {
                    PrintPrgErr(pmNotPSDigit, Chr, "");
                    Tmp = 0;
                }
                break;
            }
        }
        break;
    default:
        PrintPrgErr(pmEscCode, ESCAPE, Chr);
    }
    return(Tmp);
}


