#include "defs.h"

char DosName[] = "dos.library";

/*
 *  get_unit(number, devbase)
 *
 *  gets exclusive access (if poss) to a unit, and loads the config
 *  from ENV:dialer<num>.config
 *
 */
struct DevUnit *
get_unit(ULONG num, struct DevBase *db)
{
struct DevUnit *unit = db->b_UList.lh_Head;
struct Library *DOSBase;

    /*  See if the unit is already being used..
     */
    while( unit->du_Node.ln_Succ ) {
        if( unit->du_Number == num ) return(NULL);
        unit = unit->du_Node.ln_Succ;
    }

    DOSBase = OpenLibrary(DosName, 37);
    if( DOSBase ) {

        unit = AllocMem(sizeof(struct DevUnit), MEMF_CLEAR | MEMF_PUBLIC);
        if( unit ) {
        struct MsgPort *port = &unit->du_MsgPort;
        struct Message *msg = &unit->du_Message;

            /* After this, its safe to Wait(), since I do a check above.. */
            unit->du_Number = num;
            AddHead(&db->b_UList, &unit->du_Node);

            /* initialise the messageport */
            port->mp_Node.ln_Type = NT_MSGPORT;
            port->mp_Flags = PA_SIGNAL;
            port->mp_SigBit = AllocSignal(-1);
            port->mp_SigTask = FindTask(NULL);
            NewList(&port->mp_MsgList);

            /* initialise the process message */
            msg->mn_Node.ln_Type = NT_MESSAGE;
            msg->mn_Length = sizeof(struct DevUnit);  /* send the whole unit */
            msg->mn_ReplyPort = port;

            if( port->mp_SigBit != -1 ) {
            struct Process *proc;
            struct TagItem ConfigTagList[] = {
                {   NP_Entry,   config_proc     },
                {   NP_Name,    "Config Loader" },
                {   TAG_DONE,   TAG_DONE        }, };

                proc = CreateNewProc(ConfigTagList);
                if( proc ) {

                    PutMsg(&proc->pr_MsgPort, msg);

                    /* wait for the msg to return.. */
                    while( !GetMsg(port) )
                        WaitPort(port);
                }
            }
            else {
                free_unit(unit);
                unit = NULL;
            }
        }
        CloseLibrary(DOSBase);
    }

    return(unit);
}


/*
 *  free misc resources associated with a unit
 */
void
free_unit(struct DevUnit *unit)
{
    Remove(&unit->du_Node);

    freestring(unit->du_SerName);
    freestring(unit->du_InitString);
    freestring(unit->du_DialString);
    freestring(unit->du_Phone);
    freestring(unit->du_SendExpect);

    FreeSignal(unit->du_MsgPort.mp_SigBit);

    FreeMem(unit, sizeof(struct DevUnit));
}


/*
 *  This is the main Process routine, which finds the configuration
 *  for the unit, from ENV:
 */
void
config_proc(void)
{
struct MsgPort *port = &((struct Process *)FindTask(NULL))->pr_MsgPort;
struct DevUnit *unit;
struct Message *msg;

    /* wait for mail.. */
    while( (msg = GetMsg(port)) == NULL )
        WaitPort(port);

    /* We need to find the unit pointer; the Message is the second */
    /* item in the DevUnit structure, after the initial Node structure */
    unit = (struct DevUnit *)((ULONG)msg - sizeof(struct Node));

    /* some defaults, before the load */
    unit->du_DialTimeout = 60;
    unit->du_Timeout = 30;

    unit->du_WindowSize[0] = 320;
    unit->du_WindowSize[1] = 11;
    unit->du_WindowSize[2] = 320;
    unit->du_WindowSize[3] = 100;

    unit->du_ZoomSize[0] = 320;
    unit->du_ZoomSize[1] = 11;
    unit->du_ZoomSize[2] = 320;
    unit->du_ZoomSize[3] = 11;

    unit->du_Retries = 1;

    load_config(unit);

    /* some defaults, after the load */
    if( !unit->du_SerName )     allocstring("serial.device", &unit->du_SerName);
    if( !unit->du_InitString )  allocstring("ATZ", &unit->du_InitString);
    if( !unit->du_DialString )  allocstring("ATDT", &unit->du_DialString);

    Forbid();
    ReplyMsg(msg);
}


/*
 *  the dos.library bit, reads the whole config file into memory
 *      and parses it.
 */
void
load_config(struct DevUnit *unit)
{
struct Library *DOSBase = OpenLibrary(DosName, 37);
char cname[64];
BPTR env;

    sprintf(cname, "ENV:dialer%ld.config", unit->du_Number);
    env = Open(cname, MODE_OLDFILE);
    if( env ) {
    LONG size, *config;

        Seek(env, 0, OFFSET_END);
        size = Seek(env, 0, OFFSET_BEGINNING);

        config = AllocMem(size + 1, MEMF_CLEAR);
        if( config ) {

            if( Read(env, config, size) == size ) {
            char *line, *next = config;

                while( *(line = next) ) {
                char *keyword, *arg;

                    while( *next ) {
                        if( *next == '\n' ) {
                            *next++ = '\0';
                            break;
                        }
                        next++;
                    }

                    keyword = token(&line);
                    arg = token(&line);

                    if( !stricmp(keyword, "DEVICE") )
                        allocstring(arg, &unit->du_SerName);
                    else if( !stricmp(keyword, "UNIT") )
                        unit->du_SerUnit = atol(arg);
                    else if( !stricmp(keyword, "BAUD") )
                        unit->du_BaudRate = atol(arg);
                    else if( !stricmp(keyword, "RETRIES") )
                        unit->du_Retries = atol(arg);
                    else if( !stricmp(keyword, "DIALSTRING") )
                        allocstring(arg, &unit->du_DialString);
                    else if( !stricmp(keyword, "INITSTRING") )
                        allocstring(arg, &unit->du_InitString);
                    else if( !stricmp(keyword, "PHONE" ) )
                        allocstring(arg, &unit->du_Phone);
                    else if( !stricmp(keyword, "CONVERSE" ) ) {
                        allocstring(arg, &unit->du_SendExpect);
                        allocstring(token(&line), &unit->du_SendExpect);
                    }
                    else if( !stricmp(keyword, "DIALTIMEOUT") )
                        unit->du_DialTimeout = atol(arg);
                    else if( !stricmp(keyword, "TIMEOUT") )
                        unit->du_Timeout = atol(arg);
                    else if( !stricmp(keyword, "WINDOWSIZE") ) {
                        unit->du_WindowSize[0] = atol(arg);
                        unit->du_WindowSize[1] = atol(token(&line));
                        unit->du_WindowSize[2] = atol(token(&line));
                        unit->du_WindowSize[3] = atol(token(&line));
                    }
                    else if( !stricmp(keyword, "ZOOMSIZE") ) {
                        unit->du_ZoomSize[0] = atol(arg);
                        unit->du_ZoomSize[1] = atol(token(&line));
                        unit->du_ZoomSize[2] = atol(token(&line));
                        unit->du_ZoomSize[3] = atol(token(&line));
                    }
                    else if( !stricmp(keyword, "DEBUG") ) {
                        unit->du_Debug = TRUE;
                    }
                }
            }
            FreeMem(config, size + 1);
        }

        Close(env);
    }

    CloseLibrary(DOSBase);
}

/*
 *  return a pointer to the next token in the string, and update the
 *  string pointer.
 */
char *
token(char **addr)
{
char *line = *addr;
int q = FALSE;
char *p, *start;

    while( *line == ' ' ) line++;

    if( *line == '"' ) line++, q = TRUE;

    p = start = line;

    while( *line ) {
        if( q && *line == '"' ) break;
        if( !q && *line == ' ' ) break;
        if( *line == '\\' && line[1] ) {
            line++;
            switch(*line) {
            case 'n':   *line = '\n';
                        break;
            case 'r':   *line = '\r';
                        break;
            }
        }
        if( *line == '^' && line[1] ) {
            line++;
            if( *line >= 'a' && *line <= 'z' ) *line -= 'a' + 1;
            if( *line >= 'A' && *line <= 'Z' ) *line -= 'A' + 1;
        }
        *p++ = *line++;
    }

    if( *line ) line++;
    *addr = line;
    *p = '\0';
    return(start);
}

/*
 *  allocates a string and adds it to the list
 */
void
allocstring(char *text, char **list)
{
int size = strlen(text) + sizeof(struct slist);
struct slist *str = AllocMem(size, MEMF_PUBLIC | MEMF_CLEAR);

    if( str ) {
        str->size = size;
        strcpy(str->data, text);

        while( *list )
            list = *list - sizeof(char *);

        *list = str->data;
    }
}

/*
 *  frees a string list
 */
void
freestring(char *str)
{
    while( str ) {
    struct slist *tmp = (struct slist *)(str - sizeof(int) - sizeof(char *));

        str = tmp->next;
        FreeMem(tmp, tmp->size);
    }
}
