/*
** UMUtilities.c
** Copyright (C) 1996-97 Serge Emond
**
** 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., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
*/

#include "UrlManager.h"

/// BOOL Exists(file)
// Returns TRUE if file exists

BOOL Exists(TEXT *file)
{
    BPTR    l;

    l = Lock(file, ACCESS_READ);
    if (l)
    {
        UnLock(l);
        return(TRUE);
    }
    return(FALSE);
}
///

/// ReadAVar (template, opts_array, rdargs, stuff)
// Initialises the rdargs struct and passes the string "stuff" to ReadArgs().

// TEXT *template
//       ReadArgs template to use
// LONG *array
//      Array to put options to
// struct RDArgs *rdargs
// TEXT *p
//       Pointer to a string containing the arguments to parse

// Returns struct RDArgs * passed or NULL

struct RDArgs *ReadAVar(TEXT * template, LONG *array, struct RDArgs *rdargs, TEXT * p)
{
    TEXT *tmp;              // Work strptr
    ULONG len;              // Work length
    struct RDArgs *rda;     // Result

    len = strlen(p);

    // Checks if 'p' ends with LF
    if (p[len-1] == 10) tmp = p;
    else
    {
        // Add an LF to the string for ReadArgs to accept them
        len++;
        tmp = (TEXT *)AllocMem(len+1, NULL);    // Alloc Mem to work with
        if (!tmp) return(NULL);                 // No Alloc - return
        strncpy(tmp, p, len-1);
        tmp[len-1] = 10;
        tmp[len] = 0;
    }

    // Initialise rdargs struct to parse a variable
    rdargs->RDA_Source.CS_Buffer = tmp;
    rdargs->RDA_Source.CS_Length = len;
    rdargs->RDA_Source.CS_CurChr = NULL;
    rdargs->RDA_DAList = NULL;
    rdargs->RDA_Buffer = NULL;
    rdargs->RDA_BufSiz = NULL;
    rdargs->RDA_ExtHelp = NULL;
    rdargs->RDA_Flags = RDAF_NOPROMPT;      // We don't want prompts...

    // Parse Arguments
    rda = ReadArgs(template, array, rdargs);
    if (p == tmp) FreeMem(tmp, len+1);      // FreeMem we alloc'd
    if (rda) return(rdargs);                // All went OK: return struct
    return(NULL);
}
///

/// Search (minid, pat, type)
// Searchs an url in the list

// ULONG minid
//       Lowest ID wanted - 0 to search all
// TEXT *pat
//       AmigaDOS Pattern that must match the URL.  NULL to disable
// UBYTE type
//       Type of URL.  NULL to disable.  MUST BE UPPERCASE

// result:
//       Pointer to the url or NULL on error/nothing found

struct Url *Search(ULONG minid, TEXT * pat, UBYTE type)
{
    struct Url  *up;        // result of search
    TEXT * tok;             // Tokenized pattern
    ULONG tokl;             // Tokenized pattern's length

    if (IsNoData) return(NULL);     // No url, return now

    // Initialize Pattern Stuff
    if (pat)
    {
        tokl = strlen(pat)*2 + 2;       // Space needed to tokenize
        if (!(tok = (TEXT *)AllocMem(tokl, NULL)))
            return(NULL);
        if (ParsePatternNoCase(pat, tok, tokl)<0)
        {
            // Could not tokenize..
            FreeMem(tok, tokl);
            return(NULL);
        }
    }

    for (up = (struct Url *)g.list.mlh_Head; up->n.mln_Succ;
                                        up=(struct Url *)up->n.mln_Succ)
    {
        if (!type || up->type == type)
        {
            if (up->id >=minid)
            {
                if (pat)
                {
                    if (MatchPatternNoCase(tok, up->name))
                    {
                        break;
                    }
                }
                else break;
            }
        }
    }
    
    if (pat) FreeMem(tok, tokl);

    if (up->n.mln_Succ) return(up);
    return(NULL);
}
///

/// AddUrl (type, depth, name)
// Add an URL to the list.

// UBYTE type
//       Type of the url - must be uppercase
// LONG depth
//       Depth
// TEXT *name
//       Name of the url
//
// Result
//   -1              Url already exists in list
//   0               Failed
//   struct Url *    Added

struct Url *AddUrl(UBYTE type, LONG depth, TEXT * name)
{
    struct Url *up;

    if (MatchName(name, FALSE)) return((struct Url *)(-1));

    // Alloc space for the structure
    up = (struct Url *)AllocPooled(g.upool, sizeof(struct Url));
    if (up)
    {
        // Alloc space for the name
        up->name = (TEXT *)AllocPooled(g.spool, strlen(name)+1);
        if (up->name)
        {
            strcpy(up->name, name);
            up->depth = depth;
            up->type = type;
            up->id = ++g.hid;
            g.inmem++;

            // Add it to the list
            AddTail((struct List *)&g.list, (struct Node *)up);
        }
        else
        {
            FreePooled(g.upool, (void *)up, sizeof(struct Url));
            return(NULL);
        }
    }
    else return(NULL);

    return(up);
}
///

/// KillUrl (id)

// Kill the url with an id of "id".

// Returns TRUE if killed, FALSE if ID doesn't exist

BOOL KillUrl(ULONG id)
{
    struct Url *up;

    if (IsNoData) return(FALSE);
    if (up = GetID(id))
    {
        Remove((struct Node *)up);
        FreePooled(g.spool, (void *)up->name, strlen(up->name)+1);
        FreePooled(g.upool, (void *)up, sizeof(struct Url));
        g.inmem--;
        return(TRUE);
    }
    return(FALSE);
}
///

/// KillType (type)

// Kill all urls of the type 'type'.  type MUST be uppercase

// Returns the number of urls killed

ULONG KillType(UBYTE type)
{
    struct Url *up, *upp;
    ULONG cnt;

    if (IsNoData) return(NULL);
    cnt = 0;
    for (up = (struct Url *)g.list.mlh_Head; up->n.mln_Succ; up = upp)
    {
        upp = (struct Url *)up->n.mln_Succ;
        if (up->type == type)
        {
            Remove((struct Node *)up);
            FreePooled(g.spool, (void *)up->name, strlen(up->name)+1);
            FreePooled(g.upool, (void *)up, sizeof(struct Url));
            g.inmem--;
            cnt++;
        }
    }
    return(cnt);
}
///

/// KillPattern (pat)
// Kills all urls matching the specified AmigaDOS Pattern.

// Returns how many were killed

ULONG KillPattern(TEXT * pat)
{
    struct Url *up, *upp;
    TEXT * tok;
    ULONG cnt, tokl;

    if (IsNoData) return(NULL);
    tokl = strlen(pat)*2+2;
    if (!(tok = (TEXT *)AllocMem(tokl, NULL)))
    {
        /* Failed to alloc mem */
        return(NULL);
    }

    /* Tokenize Pattern Stuff */
    if (ParsePatternNoCase(pat, tok, tokl)<0)
    {
        FreeMem(tok, tokl);     /* Error tokenizing */
        return(NULL);
    }

    cnt = 0;
    for (up = (struct Url *)g.list.mlh_Head; up->n.mln_Succ; up = upp)
    {
        upp = (struct Url *)up->n.mln_Succ;
        if (MatchPatternNoCase(tok, up->name))
        {
            Remove((struct Node *)up);
            FreePooled(g.spool, (void *)up->name, strlen(up->name)+1);
            FreePooled(g.upool, (void *)up, sizeof(struct Url));
            g.inmem--;
            cnt++;
        }
    }
    FreeMem(tok, tokl);
    return(cnt);
}
///

/// FlushAll

// Releases memory pools & initialize url list.
// Url memory blocks contains 16 urls.  Name blocks contain 16k.

// Returns TRUE on success

BOOL FlushAll()
{
    g.list.mlh_Head = (struct MinNode *)&g.list.mlh_Tail;
    g.list.mlh_Tail = NULL;
    g.list.mlh_TailPred = (struct MinNode *)&g.list.mlh_Head;
    g.hid = 0L;
    g.inmem = 0L;

    if (g.spool) DeletePool(g.spool); g.spool = NULL;
    if (g.upool) DeletePool(g.upool); g.upool = NULL;

    if (!(g.upool = CreatePool(MEMF_CLEAR, 16*sizeof(struct Url), sizeof(struct Url))))
    {
        return(FALSE);
    }
    if (!(g.spool = CreatePool(MEMF_CLEAR, 16*1024, 256)))
    {
        DeletePool(g.upool); g.upool = NULL;
        return(FALSE);
    }
    return(TRUE);
}
///

/// GetID (id)
// Returns the url associated with an ID.  NULL if ID doesn't exist

struct Url *GetID(ULONG id)
{
    struct Url *up;

    if (IsNoData) return(NULL);
    for (up = (struct Url *)g.list.mlh_Head; up->n.mln_Succ;
                                        up = (struct Url *)up->n.mln_Succ)
    {
        if (up->id == id) break;
    }
    if (up->n.mln_Succ) return(up);
    return(NULL);
}
///

/// MatchName (mn, nocase)

// Return the url identical to mn.  Comparison if case insensitive if nocase
// is TRUE.

// TEXT *mn      Name no match
// BOOL nocase   If TRUE, matching is not case sensitive

// Returns NULL or url found

struct Url *MatchName(TEXT *mn, BOOL nocase)
{
    struct Url *up;
    int (*scmp)(const char *, const char *);

    if (IsNoData) return(NULL);
    if (nocase) scmp = stricmp;
    else scmp = strcmp;

    for (up = (struct Url *)g.list.mlh_Head; up->n.mln_Succ; up = (struct Url *)up->n.mln_Succ)
    {
        if (!scmp(up->name, mn)) break;
    }
    if (up->n.mln_Succ) return(up);
    return(NULL);
}
///

/// LoadFile (fname)

// Load a file where each line is in the following format:
//       <type> <depth> <url>

// TEXT *fname       Filename of file to load
//
// Results:
//   -1      Failed
//   other   Number of urls added

ULONG LoadFile(TEXT * fname)
{
    BPTR fh;
    TEXT line[256], *p, tok[128];
    UBYTE type;
    LONG depth;
    struct Url *up;
    ULONG cnt;

    fh = Open(fname, MODE_OLDFILE);
    if (!fh) return(-1);

    SetVBuf(fh, NULL, BUF_FULL, g.iosize);

    cnt = 0;
    while(FGets(fh, line, sizeof(line)-1))
    {
        p = stptok(line, tok, sizeof(tok), " \012");
        if (*p=='\0') break;
        p = stpblk(++p);
        type = toupper(tok[0]);

        p = stptok(p, tok, sizeof(tok), " \012");
        if (*p=='\0') break;
        p = stpblk(++p);
        depth = atol(tok);

        p = stptok(p, tok, sizeof(tok), " \012");
        up = AddUrl(type, depth, tok);
        if (!up) break;
        if ((LONG)up != -1) cnt++;
    }
    Close(fh);

    if (!up) return(-1);
    return(cnt);
}
///

/// SaveFile (fname, type, pat, append, full)

// Save urls into a file

// TEXT *fname       Filename to save to
// UBYTE type        Type to save (uppercase..)
// TEXT *pat         NULL or pattern to match urls
// BOOL append       Append to file or erase?
// BOOL full         Full save?  TRUE: format described in LoadFile,
//                   FALSE: Save only url name

// Result: FALSE if something went wrong

BOOL SaveFile(TEXT *fname, UBYTE type, TEXT *pat, BOOL append, BOOL full)
{
    BPTR fh;
    TEXT *tok;
    ULONG i, tokl;
    struct Url *up;
    BOOL ret;

//  if (IsNoData) return(FALSE);

    if (pat)
    {
        tokl = strlen(pat)*2 +2;
        if(!(tok = (TEXT *)AllocMem(tokl, NULL)))
            return(FALSE);
        if (ParsePatternNoCase(pat, tok, tokl)<0)
        {
            FreeMem(tok, tokl);
            return(FALSE);
        }
    }

    ret = Exists(fname) && append;

    fh = Open(fname, (ret ? MODE_READWRITE : MODE_NEWFILE));
    if (!fh)
    {
        if (pat) FreeMem(tok, tokl);
        return(FALSE);
    }

    SetVBuf(fh, NULL, BUF_LINE, g.iosize);

    if (ret)
    {
        Seek(fh, 0L, OFFSET_END);
        if (IoErr())
        {
            if (pat) FreeMem(tok, tokl);
            Close(fh);
            return(FALSE);
        }
    }

    for (i=1; i<=g.hid; i++)
    {
        if (!(up = GetID(i))) continue;
        if (type && up->type != type) continue;
        if (pat)
            if (!MatchPatternNoCase(tok, up->name)) continue;

        if (full)
        {
            if (FPrintf(fh, "%lc %2ld %s\n", (LONG)up->type, up->depth, up->name) < 0)
                break;
        }
        else
        {
            if (FPrintf(fh, "%s\n", up->name) < 0)
                break;
        }
    }

    if (pat) FreeMem(tok, tokl);
    Close(fh);

    if (i <= g.hid) return(FALSE);
    return(TRUE);
}
///

/// ReadFile (fname, type, depth)

// Add urls contained in a file to the url-list.  The list contains 1 url/line

// TEXT * fname      Filename of file
// UBYTE  type       Add urls using that type (uppercase..)
// LONG   depth      Add urls using this depth

// Result: number of urls added or -1 if error

ULONG ReadFile(TEXT *fname, UBYTE type, LONG depth)
{
    BPTR fh;
    TEXT line[256];
    struct Url *up;
    ULONG cnt, pos;

    fh = Open(fname, MODE_OLDFILE);
    if (!fh) return(-1);

    SetVBuf(fh, NULL, BUF_FULL, g.iosize);

    cnt = 0;
    while(FGets(fh, line, sizeof(line)-1))
    {
        pos = strlen(line)-1;
        if (line[pos]==10) line[pos] = 0;

        up = AddUrl(type, depth, line);
        if (!up) break;
        if ((LONG)up != -1) cnt++;
    }
    Close(fh);

    if (!up) return(-1);
    return(cnt);
}
///

/// CountType(type)
// Count the number of urls of a certain type

ULONG CountType(UBYTE type)
{
    struct Url *up;
    ULONG cnt;

    cnt=0L;

    if (IsNoData) return(NULL);
    for (up = (struct Url *)g.list.mlh_Head; up->n.mln_Succ;
                                        up = (struct Url *)up->n.mln_Succ)
    {
        if (up->type == type) cnt++;
    }
    return(cnt);
}
///

