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

#define VERSION "$VER: ScanHTML 1.04 "__AMIGADATE__" (c) Serge Emond"

#include "ScanHTML.h"
#include "mystrchr.h"

const TEXT *version=VERSION;
extern struct ExecBase *SysBase;

/**** NOTE: BASE on the command line MUST end with a filename OR a "/" ****/

/// InitAll
void InitAll(struct Global *a)
{
    LONG ret;
    
    /* Set Stuff to zero */
    
    memset((TEXT *)a, 0L, sizeof(struct Global));
    
    /* Read Prg's Args */
    
    a->rdargs = ReadArgs(OPT_T, a->opts, NULL);
    if (a->rdargs == NULL)
    {
        PrintFault(IoErr(), NULL);
        EndAll(RETURN_FAIL, a);
    }
    
    CHKBRK

    /* Open input file */
    
    if (!Exists((TEXT *)a->opts[OPT_IN]))
    {
        Printf("Input file doesn't exists\n");
        EndAll(RETURN_FAIL, a);
    }
    a->fhin = Open((TEXT *)a->opts[OPT_IN], MODE_OLDFILE);
    if (!a->fhin)
    {
        PrintFault(IoErr(), NULL);
        EndAll(RETURN_FAIL, a);
    }
    
    /* Set input file's buffer */
    
    if (SetVBuf(a->fhin, NULL, BUF_FULL, IN_B_SIZE))
    {
        Printf("Can't set buffered IO's buffer\n");
    }
    
    /* Init first pattern */
    
    if (a->opts[OPT_PATTERN])
    {
        a->toklen = (strlen((TEXT *)a->opts[OPT_PATTERN]) * 2) + 3;
        if ((a->tok = (TEXT *)AllocMem(a->toklen, NULL)) ==0)
        {
            Printf("Not enough memory for pattern matching\n");
            EndAll(RETURN_FAIL, a);
        }
        if (ParsePatternNoCase((TEXT *)a->opts[OPT_PATTERN], a->tok, a->toklen)<0)
        {
            Printf("Error parsing pattern\n");
            EndAll(RETURN_FAIL, a);
        }
    }
    
    /* Init second pattern */
    
    if (a->opts[OPT_PATTERN2])
    {
        a->tok2len = (strlen((TEXT *)a->opts[OPT_PATTERN2]) * 2) + 3;
        if ((a->tok2 = (TEXT *)AllocMem(a->tok2len, NULL)) ==0)
        {
            Printf("Not enough memory for pattern matching\n");
            EndAll(RETURN_FAIL, a);
        }
        if (ParsePatternNoCase((TEXT *)a->opts[OPT_PATTERN2], a->tok2, a->tok2len)<0)
        {
            Printf("Error parsing second pattern\n");
            EndAll(RETURN_FAIL, a);
        }
    }
    
    /* Open output file */
    
    ret = Exists((TEXT *)a->opts[OPT_OUT]) && a->opts[OPT_APPEND];
    
    a->fhout = Open((TEXT *)a->opts[OPT_OUT],
        (ret ? MODE_READWRITE : MODE_NEWFILE));
    
    if (!a->fhout)
    {
        PrintFault(IoErr(), NULL);
        EndAll(RETURN_FAIL, a);
    }
    
    /* Set output file's buffer */
    
    if (SetVBuf(a->fhout, NULL, BUF_FULL, OUT_B_SIZE))
    {
        Printf("Can't set buffered IO's buffer\n");
    }
    
    if (ret) /* Jump at eof */
    {
        Seek(a->fhout, 0L, OFFSET_END);
        if (IoErr())
        {
            PrintFault(IoErr(), NULL);
            EndAll(RETURN_FAIL, a);
        }
    }
    
    /* Strip the last '/' of "base" */
    
    if (a->opts[OPT_BASE])
    {
        strcpy(a->base, (TEXT *)a->opts[OPT_BASE]);
        if (KillLastSlash(a->base) == NULL)
        {
            Printf("BASE does not contain any '/'!\n");
            EndAll(RETURN_FAIL, a);
        }
    }
}
///

/// EndAll
void EndAll(LONG ret, struct Global *a)
{
    if (a->rdargs) FreeArgs(a->rdargs);
    if (a->fhin) Close(a->fhin);
    if (a->fhout) Close(a->fhout);
    if (a->tok) FreeMem(a->tok, a->toklen);
    if (a->tok2) FreeMem(a->tok2, a->tok2len);
    exit(ret);
}
///

/// Exists
// Checks the existence of a file

BOOL Exists(TEXT *file)
{
    static BPTR    l;
    
    l = Lock(file, ACCESS_READ);
    if (l)
    {
        UnLock(l);
        return(TRUE);
    }
    return(FALSE);
}
///

/// PutUrl
BOOL PutUrl(struct Global *a)
{
	static char *cp;

    /* If NOQUERY, don't keep urls containing '?' */
    
    if (a->opts[OPT_NOQUERY])
    {
        if (strchr(a->url, '?')) return(TRUE);
    }
    
    /* If NOFIRST#, don't keep urls beginning with '#' */
    
    if (a->opts[OPT_NOFN])
    {
        if (a->url[0] == '#') return(TRUE);
    }
    
    /* If STRIP#, replace '#' with NULL '#' */
    
    if (a->opts[OPT_STRIPN])
    {
        if (cp = strchr(a->url, '#')) cp[0] = '\0';
    }
    
    /* Complete Url before pattern matching */
    
    if (!RePathUrl(a))
    {
        Printf("Error completing URL address, skipping..\n");
        return(TRUE);
    }
    
    /* First pattern match */
    
    if (a->opts[OPT_PATTERN])
    {
        if (!MatchPatternNoCase(a->tok, a->url))
        {
            if (IoErr())
            {
                PrintFault(IoErr(), NULL);
                return(FALSE);
            }
            return(TRUE);
        }
    }
    
    /* Second pattern match */
    
    if (a->opts[OPT_PATTERN2])
    {
        if (!MatchPatternNoCase(a->tok2, a->url))
        {
            if (IoErr())
            {
                PrintFault(IoErr(), NULL);
                return(FALSE);
            }
            return(TRUE);
        }
    }
    
    if (FPuts(a->fhout, a->url) != 0)
    {
        PrintFault(IoErr(), NULL);
        return(FALSE);
    }
    if (FPutC(a->fhout, 10L) != 10L)
    {
        PrintFault(IoErr(), NULL);
        return(FALSE);
    }
    return(TRUE);
}
///

/// RePathUrl
//      Get full url by interpreting unix paths

// Types:
//
//  :       (Present) do nothing
//  /       Go to root
//  ./      Stay here
//  ../     Back one dir
//  -       If none of the above, change the name

BOOL RePathUrl(struct Global *a)
{
    static TEXT url[U_SIZE], *tmp1, *tmp2;
    
    /* If true, either there's no BASE or the url is already complete */
    if (a->opts[OPT_BASE] == NULL || strchr(a->url,':') != NULL) return(TRUE);
    
    strcpy(url, a->base);
    
    /* Simply add the name.. */
    if (a->url[0] != '/' && a->url[0] != '.')
    {
        strcat(url, "/");
        strcat(url, a->url);
        strcpy(a->url, url);
    }
    else if (a->url[0] == '/')      /* Test for '/' */
    {
        /* We should clear after the third slash.. */
        tmp1 = strchr(url, '/') + 1;
        tmp2 = strchr(tmp1, '/') + 1;
        tmp1 = strchr(tmp2, '/');
        tmp1[0] = NULL;
        
        strcat(url, a->url);
        strcpy(a->url, url);
    }
    else
    {
        while(a->url[0] == '.')
        {
            if (a->url[1] == '/' || a->url[1] == '\0')       /* Got "." or "./" */
            {
                if (a->url[1]) KillFirstSlash(a->url);
                else a->url[0] = '\0';
            }
            else if(a->url[1] == '.' && (a->url[2] == '/' || a->url[2] == '\0')) /* Got "../" or ".." */
            {
                if (a->url[2]) KillFirstSlash(a->url);
                else a->url[0] = '\0';
                KillLastSlash(url);
            }
            else
            {
                Printf("Url beginning with '..' not followed by '%.1s'!?\n", a->url+2);
                return(FALSE);
            }
        }
        /* We add what's left.. */
        strcat(url, "/");
        strcat(url, a->url);
        strcpy(a->url, url);
    }
    
    return(TRUE);
}
///

/// KillLastSlash
//      Removes the last slash and following of a string

TEXT *KillLastSlash(TEXT *str)
{
    static TEXT *lastslash, *tmpptr;
    
    if ((lastslash = strchr(str, '/')) == NULL)
    {
        return(NULL);       /* Hmm.. */
    }
    
    while (tmpptr = strchr(lastslash+1, '/'))
        lastslash = tmpptr;
    lastslash[0] = 0;
    return(str);
}
///

/// KillFirstSlash
//      Kills first slash and what preceeds it.

TEXT *KillFirstSlash(TEXT *str)
{
    static TEXT *firstslash;
    
    if ((firstslash = strchr(str, '/')) == NULL)
    {
        return(str);        /* No slash! */
    }
    
    strcpy(str, firstslash+1);
    return(str);
}
///

/// GetNext
// Looks for "<", read up to ">"
// Returns TRUE & string between "<" & ">"
// Returns FALSE if EOF or error

BOOL GetNext(struct Global *a)
{
    static TEXT c;
    static LONG pos;
    
	pos = 0;
    while (1)
    {
        CHKBRK
        c = FGetC(a->fhin);
        if (c==(LONG)'<' || !(c>=0 && c<255)) break;
    }
    
    if (c!=(LONG)'<') return(FALSE);
    
    while (pos < S_SIZE-1)
    {
        CHKBRK
        c = FGetC(a->fhin);
        if (c==(LONG)'>' || !(c>=0 && c<255)) break;
        a->str[pos] = c;
        pos++;
    }
    
    /* Return false if buffer full or not ASCII */
    
    if (!(pos < S_SIZE-1)) Printf("Buffer Overflow\n");
    if (!(pos < S_SIZE-1) || !(c>=0 && c<255)) return(FALSE);
    
    a->str[pos] = NULL;
    
    return(TRUE);
}
///

/// stristr
// search a string in a string - semi-case insensitive
char *stristr(char *str, char *pat)
{
    static char tmp[S_SIZE], *ptr;
    static int cnt;

    cnt = 0;

    while (str[cnt]) {
        tmp[cnt]=toupper(str[cnt]);
        cnt++;
    }
    tmp[cnt]=0;

    ptr = strstr(tmp, pat);
    if (ptr)
        return((char *)((int)ptr-(int)tmp + (int)str));

    return(0);
}
///

/// GetUrl
// Returns FALSE if nothing known is found

BOOL GetUrl(struct Global *a)
{
    static TEXT *tag, *beg, *end, *end2, endchar;
    
    if (a->opts[OPT_NOBG] || !(tag = stristr(a->str, "BACKGROUND")))
    {
        if (a->opts[OPT_NOSRC] || !(tag = stristr(a->str, "SRC")))
        {
            if (a->opts[OPT_NOHREF] || !(tag = stristr(a->str, "HREF")))
            {
                return(FALSE);
            }
        }
    }
    
    // First determine if there are " around the url..

    beg = strchr(tag, '=');
    if (beg == NULL) return(FALSE);
    beg++;
    while (1) {
        // Search for first non-space char
        if (beg[0] != ' ')
            break;
        if (beg[0] == NULL)
            break;
        beg++;
    }
    if (beg[0] == '"') {
        endchar = '"';
        beg++;
    }
    else
        endchar = ' ';

//    beg = strchr(tag, '"')+1;

    end = strchr(beg, endchar);
    end2 = strchr(beg, '\r');
    if (end2 && ((end2 < end)||(!end)))
        end = end2;

    end2 = strchr(beg, '\n');
    if (end2 && ((end2 < end)||(!end)))
        end = end2;

    if (end == beg) return(FALSE);
    if (end == NULL) {
        strcpy(a->url, beg);
    }
    else {
        strncpy(a->url, beg, (size_t)(end-beg));
        a->url[(LONG)(end-beg)] = 0;
    }
    
    return(TRUE);
}
///

/// Main
LONG main()
{
    struct Global a;
    
    if (SysBase->LibNode.lib_Version < 37)
    {
        Write(Output(), "You need OS version 37 or more\n", 31);
        exit(20);
    }
    
    InitAll(&a);
    
    while (GetNext(&a))
    {
        if (GetUrl(&a))
        {
            if (!PutUrl(&a))
                EndAll(RETURN_FAIL, &a);
        }
    }
    
    EndAll(0L, &a);
}
///

