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

HDEnv 1.2 (1.9.95)
Copyright (C) 1995 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>


#define VERSIONTAG "$VER: HDEnv 1.2 " __AMIGADATE__

#define ERR_BREAK 10001

#define COPYBUF 2048


struct HDEnv {

    char *path;
    struct FileInfoBlock *fib;
    void *copybuf;
    long test,verbose;
};


const char *versiontag = VERSIONTAG;

extern long __oslibversion = 37;

struct HDEnv *hde;

long error_code = 0;


void __regargs _CXBRK(void) {

    error_code = ERR_BREAK;
}


void free_all(struct HDEnv *hde) {

    if(hde->path) FreeMem(hde->path,256);
    if(hde->fib) FreeDosObject(DOS_FIB,hde->fib);
    if(hde->copybuf) FreeMem(hde->copybuf,COPYBUF);

    FreeMem(hde,sizeof(struct HDEnv));
}

struct HDEnv *alloc_all(void) {

    struct HDEnv *hde;

    if(hde = AllocMem(sizeof(struct HDEnv),MEMF_CLEAR)) {
        if(!(hde->path = AllocMem(256,MEMF_CLEAR))) error_code = ERROR_NO_FREE_STORE;
        if(!(hde->fib = AllocDosObject(DOS_FIB,NULL))) error_code = ERROR_NO_FREE_STORE;
        if(!(hde->copybuf = AllocMem(COPYBUF,MEMF_CLEAR))) error_code = ERROR_NO_FREE_STORE;
    }
    else error_code = ERROR_NO_FREE_STORE;

    return hde;
}


void del_file(char *name) {

    BPTR l;

    strmfp(hde->path,"ENVARC:",name);
    if(!(l = Lock(hde->path,ACCESS_READ))) {
        if(hde->test) Printf("ENV:%s\n",name);
        else {
            if(hde->verbose) Printf("deleting ENV:%s...\n",name);
            if(!(DeleteFile(name))) error_code = IoErr();
        }
    }
    else UnLock(l);
}

void del_dir(char *dir) {

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

    if(fib = AllocDosObject(DOS_FIB,NULL)) {
        if(path = AllocMem(256,MEMF_CLEAR)) {
            if(l = Lock(dir,ACCESS_READ)) {
                rc = Examine(l,fib);
                if(rc) rc = ExNext(l,fib);
                while(rc) {
                    strmfp(path,dir,fib->fib_FileName);
                    if(fib->fib_DirEntryType > 0) del_dir(path);
                    else del_file(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);
            }
            if(!error_code) {
                strmfp(path,"ENVARC:",dir);
                if(!(l = Lock(path,ACCESS_READ))) {
                    if(hde->test) Printf("ENV:%s (dir)\n",dir);
                    else {
                        if(hde->verbose) Printf("deleting ENV:%s...\n",dir);
                        if(!(DeleteFile(dir))) error_code = IoErr();
                    }
                }
                else UnLock(l);
            }
            FreeMem(path,256);
        }
        else error_code = ERROR_NO_FREE_STORE;
        FreeDosObject(DOS_FIB,fib);
    }
    else error_code = ERROR_NO_FREE_STORE;
}


void copy_file(char *name,struct FileInfoBlock *src_fib) {

    BOOL copy = FALSE;
    BPTR l,src_fh,dest_fh;
    long size,bufsize,allocsize;
    void *buf=NULL;

    strmfp(hde->path,"ENV:",name);
    if(!(l = Lock(hde->path,ACCESS_READ))) copy = TRUE;
    else {
        Examine(l,hde->fib);
        if(CompareDates(&src_fib->fib_Date,&hde->fib->fib_Date) != 0)
            copy = TRUE;
        UnLock(l);
    }

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

void copy_dir(char *dir) {

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

    if(fib = AllocDosObject(DOS_FIB,NULL)) {
        if(path = AllocMem(256,MEMF_CLEAR)) {
            strmfp(path,"ENV:",dir);
            if(!(l = Lock(path,ACCESS_READ))) {
                if(hde->test) Printf("%s (created)\n",path);
                else {
                    if(hde->verbose) Printf("creating %s...\n",path);
                    l = CreateDir(path);
                }
            }

            if(l || hde->test) {
                if(l) UnLock(l);
                if(l = Lock(dir,ACCESS_READ)) {
                    rc = Examine(l,fib);
                    if(rc) rc = ExNext(l,fib);
                    while(rc) {
                        strmfp(path,dir,fib->fib_FileName);
                        if(fib->fib_DirEntryType > 0) copy_dir(path);
                        else copy_file(path,fib);
                        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);
                }
            }
            else error_code = IoErr();
            FreeMem(path,256);
        }
        else error_code = ERROR_NO_FREE_STORE;
        FreeDosObject(DOS_FIB,fib);
    }
    else error_code = ERROR_NO_FREE_STORE;
}


int main(void) {

    struct RDArgs *rda;
    long arg[2] = { 0,0 };
    BPTR l,old = NULL;


    hde = alloc_all();

    if(!error_code) {
        if(rda = ReadArgs("TEST/S,VERBOSE/S",arg,NULL)) {
            hde->test = arg[0];
            hde->verbose = arg[1];
            FreeArgs(rda);
            if(l = Lock("ENV:",ACCESS_READ)) {
                old = CurrentDir(l);
                if(hde->test) Printf("Would have deleted:\n");
                del_dir("");
                UnLock(l);
            }
            else error_code = IoErr();
        }
        else error_code = IoErr();

        if(!error_code) {
            if(l = Lock("ENVARC:",ACCESS_READ)) {
                CurrentDir(l);
                if(hde->test) Printf("Would have copied:\n");
                copy_dir("");
                UnLock(l);
            }
            else error_code = IoErr();
        }

        if(old) CurrentDir(old);
    }

    if(hde) free_all(hde);

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

    return RETURN_OK;
}
