/*****************************************************************************\

HDEnv 1.4 (10.4.96)
Copyright (C) 1995/96 by Michael Fedrowitz <mfedrowi@ix.urz.uni-heidelberg.de>

    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.

\*****************************************************************************/


#include <exec/types.h>
#include <exec/memory.h>
#include <dos/dos.h>
#include <proto/dos.h>
#include <proto/exec.h>
#include <stdlib.h>
#include <strings.h>
#include <dos.h>


#ifndef V39
void * __asm AsmCreatePool(register __d0 ULONG,register __d1 ULONG,register __d2 ULONG,register __a6 struct ExecBase *);
void __asm AsmDeletePool(register __a0 void *,register __a6 struct ExecBase *);
void * __asm AsmAllocPooled(register __a0 void *,register __d0 ULONG,register __a6 struct ExecBase *);
void __asm AsmFreePooled(register __a0 void *,register __a1 void *,register __d0 ULONG,register __a6 struct ExecBase *);

#define CreatePool(memFlags,puddleSize,threshSize) AsmCreatePool(memFlags,puddleSize,threshSize,SysBase)
#define DeletePool(poolHeader) AsmDeletePool(poolHeader,SysBase)
#define AllocPooled(poolHeader,memSize) AsmAllocPooled(poolHeader,memSize,SysBase)
#define FreePooled(poolHeader,memory,memSize) AsmFreePooled(poolHeader,memory,memSize,SysBase)
#endif

#define VERSIONTAG "$VER: HDEnv 1.4 " __AMIGADATE__

#define TEMPLATE "IGNORE/K/M,ALL/S,VERBOSE/S,NODEL/S,NOCOPY/S,TEST/S"

#define ARG_IGNORE  0
#define ARG_ALL     1
#define ARG_VERBOSE 2
#define ARG_NODEL   3
#define ARG_NOCOPY  4
#define ARG_TEST    5

#define ERR_BREAK 10001

#define COPYBUF 2048


struct File {

    struct File *next,*prev;
    struct FileInfoBlock fib;
    char path[1];
};

struct Dir {

    struct File *first,*last;
};

struct HDEnv {

    void *pool;
    struct Dir env,envarc,ignore;
    char path[256];
    char copybuf[COPYBUF];
    long verbose,test;
};


#ifdef V39
const long __oslibversion = 39;
#else
const long __oslibversion = 37;
#endif

const char *versiontag = VERSIONTAG;

struct HDEnv *hde = NULL;

long error_code = 0;


void __regargs _CXBRK(void) {

    error_code = ERR_BREAK;
}


void free_all(void) {

    DeletePool(hde->pool);
    FreeMem(hde,sizeof(struct HDEnv));
}

struct HDEnv *alloc_all(void) {

    struct HDEnv *hde;

    if(hde = AllocMem(sizeof(struct HDEnv),MEMF_CLEAR)) {
        if(!(hde->pool = CreatePool(MEMF_CLEAR,10000,10000))) {
            FreeMem(hde,sizeof(struct HDEnv));
            hde = NULL;
        }
    }
    if(!hde) error_code = ERROR_NO_FREE_STORE;

    return hde;
}


void add_file(struct Dir *dir,char *path,struct FileInfoBlock *fib) {

    struct File *file;

    if(file = AllocPooled(hde->pool,sizeof(struct File)+strlen(path))) {
        if(fib) memcpy(&file->fib,fib,sizeof(struct FileInfoBlock));
        strcpy(file->path,path);
        if(dir->first) {
            dir->last->next = file;
            file->prev = dir->last;
        }
        else dir->first = file;
        dir->last = file;
    }
    else error_code = ERROR_NO_FREE_STORE;
}

void scan_dir(struct Dir *dir,char *name) {

    BOOL rc;
    BPTR l;
    long err;
    char *path;
    struct FileInfoBlock *fib;

    if(fib = AllocDosObject(DOS_FIB,NULL)) {
        if(path = AllocPooled(hde->pool,256)) {
            if(l = Lock(name,ACCESS_READ)) {
                rc = Examine(l,fib);
                if(rc) rc = ExNext(l,fib);
                while(rc) {
                    strmfp(path,name,fib->fib_FileName);
                    add_file(dir,path,fib);
                    if(fib->fib_DirEntryType > 0) scan_dir(dir,path);
                    chkabort();
                    if(error_code) break;
                    rc = ExNext(l,fib);
                }
                if(!error_code) {
                    err = IoErr();
                    if(err != ERROR_NO_MORE_ENTRIES) error_code = err;
                }
                UnLock(l);
            }
            FreePooled(hde->pool,path,256);
        }
        else error_code = ERROR_NO_FREE_STORE;
        FreeDosObject(DOS_FIB,fib);
    }
    else error_code = ERROR_NO_FREE_STORE;
}

struct File *find_file(struct Dir *dir,char *name) {

    struct File *file;

    file = dir->first;

    while(file) {
        if(!(stricmp(file->path,name))) break;
        file = file->next;
    }

    return file;
}


void del_files(void) {

    struct File *file;

    file = hde->env.last;

    while(file) {
        if(!find_file(&hde->ignore,file->path)) {
            if(!(find_file(&hde->envarc,file->path))) {
                strmfp(hde->path,"ENV:",file->path);
                if(hde->test) Printf("%s\n",hde->path);
                else {
                    if(hde->verbose) Printf("deleting %s...\n",hde->path);
                    if(!(DeleteFile(hde->path))) error_code = IoErr();
                }
            }
        }
        chkabort();
        if(error_code) break;
        file = file->prev;
    }
}


void copy_files(void) {

    struct File *file,*dest;
    BOOL copy;
    BPTR l,src_fh,dest_fh;
    long size,allocsize,bufsize,rc;
    void *buf;

    file = hde->envarc.first;

    while(file) {
        copy = FALSE;
        if(!find_file(&hde->ignore,file->path)) {
            strmfp(hde->path,"ENV:",file->path);
            if(!(dest = find_file(&hde->env,file->path))) {
                if(file->fib.fib_DirEntryType > 0) {
                    if(hde->test) Printf("%s (created)\n",hde->path);
                    else {
                        if(hde->verbose) Printf("creating %s...\n",hde->path);
                        if(!(l = CreateDir(hde->path))) {
                            error_code = IoErr();
                            break;
                        }
                        else UnLock(l);
                    }
                }
                else copy = TRUE;
            }
            else if(file->fib.fib_DirEntryType < 0) {
                if(CompareDates(&file->fib.fib_Date,&dest->fib.fib_Date)) copy = TRUE;
            }
        }

        if(copy) {
            if(hde->test) Printf("ENVARC:%s to %s\n",file->path,hde->path);
            else {
                if(hde->verbose) Printf("copying ENVARC:%s to %s...\n",file->path,hde->path);
                if(src_fh = Open(file->path,MODE_OLDFILE)) {
                    if(dest_fh = Open(hde->path,MODE_NEWFILE)) {
                        buf = NULL;
                        size = file->fib.fib_Size;
                        if(size > COPYBUF) {
                            if(buf = AllocPooled(hde->pool,size)) {
                                allocsize = size;
                                bufsize = size;
                            }
                        }
                        if(!buf) {
                            buf = hde->copybuf;
                            bufsize = COPYBUF;
                            allocsize = 0;
                        }
                        while(size > 0) {
                            if(size < bufsize) bufsize = size;
                            rc = Read(src_fh,buf,bufsize);
                            if(rc == bufsize) rc = Write(dest_fh,buf,bufsize);
                            if(rc != bufsize) {
                                error_code = IoErr();
                                break;
                            }
                            if(size < bufsize) bufsize = size;
                            size -= bufsize;
                        }
                        if(allocsize) FreePooled(hde->pool,buf,allocsize);
                        Close(dest_fh);
                        if(error_code) DeleteFile(hde->path);
                        else {
                            rc = SetFileDate(hde->path,&file->fib.fib_Date);
                            rc &= SetProtection(hde->path,file->fib.fib_Protection);
                            rc &= SetComment(hde->path,file->fib.fib_Comment);
                        }
                        if(!rc) error_code = IoErr();
                    }
                    else error_code = IoErr();
                    Close(src_fh);
                }
                else error_code = IoErr();
            }
        }
        chkabort();
        if(error_code) break;
        file = file->next;
    }
}


int main(void) {

    struct RDArgs *rda;
    char **ignore;
    long arg[6] = { 0,0,0,0,0,0 };
    BPTR env,envarc,old;
    int i;

    if(hde = alloc_all()) {
        if(rda = ReadArgs(TEMPLATE,arg,NULL)) {
            hde->verbose = arg[ARG_VERBOSE];
            hde->test = arg[ARG_TEST];
            if(env = Lock("ENV:",ACCESS_READ)) {
                if(envarc = Lock("ENVARC:",ACCESS_READ)) {
                    old = CurrentDir(env);
                    scan_dir(&hde->env,"");
                    CurrentDir(envarc);
                    scan_dir(&hde->envarc,"");
                    if(!arg[ARG_ALL]) {
                        add_file(&hde->ignore,"Kickstart",NULL);
                        add_file(&hde->ignore,"Workbench",NULL);
                        add_file(&hde->ignore,"Language",NULL);
                    }
                    if(ignore = (char **)arg[ARG_IGNORE]) {
                        i=0;
                        while(ignore[i]) add_file(&hde->ignore,ignore[i++],NULL);
                    }
                    if(!error_code && !arg[ARG_NODEL]) {
                        if(hde->test) Printf("Would have deleted:\n");
                        del_files();
                    }
                    if(!error_code && !arg[ARG_NOCOPY]) {
                        if(hde->test) Printf("Would have copied:\n");
                        copy_files();
                    }
                    CurrentDir(old);
                    UnLock(envarc);
                }
                else error_code = IoErr();
                UnLock(env);
            }
            else error_code = IoErr();
            FreeArgs(rda);
        }
        else error_code = IoErr();
        free_all();
    }

    if(error_code) {
        if(error_code != ERR_BREAK) {
            PrintFault(error_code,"HDEnv");
            return RETURN_FAIL;
        }
        else PutStr("*** Break\n");
    }

    return RETURN_OK;
}
