#include <string.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <time.h>

#include "../src/alf.h"

#include <errno.h>
#ifdef MS_WIN32
#include <io.h>
#include <direct.h>
#define L "I64"
#define STDERR stdout
#else
#include <unistd.h>
#include <dirent.h>
#define L "L"
#define STDERR stderr
#endif

#ifdef DMALLOC
#include "dmalloc.h"
#endif

#define DIM(a) (sizeof(a)/sizeof(a[0]))

#ifdef MS_WIN32
#define getcwd _getcwd
#endif
void
zapdir(const char *directory) {
#ifndef MS_WIN32
    /* this is not portable! relies on GNU getcwd semantics! */
    char *cwd =  getcwd (NULL, 0);
    DIR *dir;
    struct dirent *entry;

    if (chdir(directory) == -1) {
        free(cwd);
        return;
    }

    dir = opendir(".");
    entry = readdir(dir);
    while (entry != NULL) {
        if (entry->d_name[0] == '.')
            if (entry->d_name[1] == '\0' ||
                (entry->d_name[1] == '.' && entry->d_name[2] == '\0'))
                goto next;
        unlink(entry->d_name);
    next:
        entry = readdir(dir);
    }
    closedir(dir);
    chdir(cwd);
    rmdir(directory);
    free(cwd);
#else
    char            cwd [255];
    char   *		filepattern = NULL;
    HANDLE			hFindFile;
    WIN32_FIND_DATA FileData;
    BOOL            status;
	filepattern = malloc(strlen(directory)+40);
    
	GetCurrentDirectory (255, cwd);

    if (SetCurrentDirectory(directory) == 0) {
        return;
    }
    strcpy(filepattern, directory);
    strcat(filepattern, "\\*");
	hFindFile = FindFirstFile (filepattern, &FileData);
	status = hFindFile != INVALID_HANDLE_VALUE;
    while (status) {
        if (FileData.cFileName [0] == '.')
            if (  FileData.cFileName [1] == '\0' 
			   || (  FileData.cFileName [1] == '.' 
				  && FileData.cFileName [2] == '\0'
				  )
			    )
                 goto next;
        DeleteFile (FileData.cFileName);
    next:
        status = FindNextFile (hFindFile, &FileData);
	}
	FindClose           (hFindFile);
	SetCurrentDirectory (cwd);
    RemoveDirectory     (directory);
#endif
}

#if 0
#define MAXBYTESIO 100000
static void
myfprintf(FILE *out, const char *fmt,
          LONG_LONG a1, LONG_LONG a2, LONG_LONG a3, LONG_LONG a4, LONG_LONG a5) {
    char buf[1000];
    static unsigned int bytesprinted = 0;
    static time_t starttime = (time_t)0;

    if (starttime == (time_t)0)
        starttime = time(0);
    else {
        if (time(0)-starttime > (time_t)10) {
            fprintf(STDERR, "Too much time\n");
            exit(1);
        }
    };

    sprintf(buf, fmt, a1, a2, a3, a4, a5);
    bytesprinted += strlen(buf);
    if (bytesprinted > MAXBYTESIO) {
        fprintf(STDERR, "Too much I/O\n");
        exit(1);
    }
    fprintf(out, buf);
}

static void
myprintf(const char *fmt, int a1, int a2, int a3, int a4, int a5) {
    myfprintf(stdout, fmt, a1, a2, a3, a4, a5);
}

#define print0(fmt)           myprintf(fmt,0,0,0,0,0)
#define print1(fmt,a)         myprintf(fmt,a,0,0,0,0)
#define print2(fmt,a,b)       myprintf(fmt,a,b,0,0,0)
#define print3(fmt,a,b,c)     myprintf(fmt,a,b,c,0,0)
#define print4(fmt,a,b,c,d)   myprintf(fmt,a,b,c,d,0)
#define print5(fmt,a,b,c,d,e) myprintf(fmt,a,b,c,d,e)
#define fprint0(out,fmt)           myfprintf(out,fmt,0,0,0,0,0)
#define fprint1(out,fmt,a)         myfprintf(out,fmt,a,0,0,0,0)
#define fprint2(out,fmt,a,b)       myfprintf(out,fmt,a,b,0,0,0)
#define fprint3(out,fmt,a,b,c)     myfprintf(out,fmt,a,b,c,0,0)
#define fprint4(out,fmt,a,b,c,d)   myfprintf(out,fmt,a,b,c,d,0)
#define fprint5(out,fmt,a,b,c,d,e) myfprintf(out,fmt,a,b,c,d,e)
#else
#define print0(fmt)           printf(fmt)
#define print1(fmt,a)         printf(fmt,a)
#define print2(fmt,a,b)       printf(fmt,a,b)
#define print3(fmt,a,b,c)     printf(fmt,a,b,c)
#define print4(fmt,a,b,c,d)   printf(fmt,a,b,c,d)
#define print5(fmt,a,b,c,d,e) printf(fmt,a,b,c,d,e)
#define fprint0(out,fmt)           fprintf(out,fmt)
#define fprint1(out,fmt,a)         fprintf(out,fmt,a)
#define fprint2(out,fmt,a,b)       fprintf(out,fmt,a,b)
#define fprint3(out,fmt,a,b,c)     fprintf(out,fmt,a,b,c)
#define fprint4(out,fmt,a,b,c,d)   fprintf(out,fmt,a,b,c,d)
#define fprint5(out,fmt,a,b,c,d,e) fprintf(out,fmt,a,b,c,d,e)
#endif

int
main (int argc, char **argv) {
    ALF *f;
    char *line = "here are some bytes\n";
    char buf[1000];
    char buf2[1000];
    int nbytes;
#ifdef MS_WIN32
    char *file1 = "p:\\skip\\alf\\foo1";
    char *file2 = "p:\\skip\\alf\\foo2";
#else
    char *file1 = "./foo1";
    char *file2 = "./foo2";
#endif
    unsigned char c;
    unsigned int i;
    unsigned LONG_LONG len;
    int test = 0;
    int ncalls;

#ifndef MS_WIN32
    setvbuf(stderr, NULL, _IOLBF, 0);
    setvbuf(stdout, NULL, _IOLBF, 0);
#endif

    /* possible clean up from previous run */
    zapdir(file1);
    zapdir(file2);

    /****************************************************/

    printf("%2d. supports large files? %2d\n",
           ++test, alf_supports_large_files());

    /****************************************************/

    print1("%2d. testing basic write and seek beyond EOF.\n", ++test);

    f = alf_open(file1, "wb", 0);
    if (f == NULL) {
        fprint2(STDERR, "** open %s for write failed: %d.\n",
                file1, errno);
        return 1;
    }
    nbytes = alf_write(line, 1, strlen(line), f);
    print1("    wrote: %2d bytes\n", nbytes);
    if (alf_seek(f, 4000, SEEK_SET) == -1) {
        fprint0(STDERR, "** seek to offset 4000 failed.\n");
        return 1;
    }
    print2("    seeked to position %" L "d (chunk: %2d)\n", alf_tell(f),
           f->current_chunk);

    nbytes = alf_write(line, 1, strlen(line), f);
    print1("    wrote: %2d bytes\n", nbytes);
    alf_close(f);

    /****************************************************/

    print1("%2d. testing basic read.\n", ++test);

    f = alf_open(file1, "r", 0);
    if (f == NULL) {
        fprint1(STDERR, "** open for read failed: %d.\n", errno);
        return 1;
    }
    if (alf_seek(f, 0, SEEK_END) == -1) {
        fprint1(STDERR, "** seek to end of file failed: %d.\n", errno);
        return 1;
    }
    len = alf_tell(f);
    nbytes = 0;
    alf_rewind(f);
    ncalls = 0;
    while (alf_eof(f) == 0) {
        print4("    nbytes: %d, len: %" L "d, feof: %d, alf_eof: %d,",
               nbytes, len, feof(f->current), alf_eof(f));
        print2(" chunks: (%d of %d).\n", f->current_chunk+1, f->nchunks);
        if (feof(f->current) && f->current_chunk == f->nchunks-1) {
            fprint0(STDERR, "** alf_eof reports not @ eof but feof does.\n");
            return 1;
        }
        if (nbytes > len) {
            fprint2(STDERR, "** past apparent eof (%d > %" L "d), but alf_eof"
                    " and feof reports not @ eof.\n", nbytes, len);
            return 1;
        }
        nbytes += alf_read(buf, 1, DIM(buf)-1, f);
        if (errno) {
            fprint0(STDERR, "** errno set during alf_read.\n");
            return 1;
        }
        if (ncalls++ > 50) {
            fprint0(STDERR, "** alf_read loop should have ended by now.\n");
            return 1;
        }
            
    }
    print2("    reading to eof read %2d bytes.  File has length %lld.\n",
           nbytes, len);
    alf_close(f);

    /****************************************************/

    print1("%2d. testing seek within file.\n", ++test);

    f = alf_open(file1, "r", 0);
    if (f == NULL) {
        fprint1(STDERR, "** open for read failed: %d.\n", errno);
        return 1;
    }
    if (alf_seek(f, 3950, SEEK_SET) == -1) {
            fprint0(STDERR, "** seek to offset 3950 failed.\n");
            return 1;
    }
    print2("    seeked to position %" L "d (chunk: %2d)\n", alf_tell(f),
           f->current_chunk);
    nbytes = alf_read(buf, 1, DIM(buf)-1, f);
    buf[nbytes] = '\0';
    print2("    read: %2d bytes: %s\n", nbytes, buf);
    alf_close(f);

    /****************************************************/

    print1("%2d. testing r+ capability.\n", ++test);

    f = alf_open(file1, "rb+", 0);
    if (f == NULL) {
        fprint1(STDERR, "** open for rb+ failed: %d.\n", errno);
        return 1;
    }
    if (alf_seek(f, 3950, SEEK_SET) == -1) {
            fprint0(STDERR, "** seek to offset 3950 failed.\n");
            return 1;
    }
    print2("    seeked to position %" L "d (chunk: %2d)\n", alf_tell(f),
           f->current_chunk);
    nbytes = alf_write(line, 1, strlen(line), f);
    nbytes += alf_write(line, 1, strlen(line), f);
    nbytes += alf_write(line, 1, strlen(line), f);
    print1("    wrote: %2d bytes\n", nbytes);

    if (alf_seek(f, -nbytes, SEEK_CUR) == -1) {
            fprint1(STDERR, "** seek to offset %2d from current pos failed.\n",
                    -nbytes);
            return 1;
    }

    print2("    seeked to position %" L "d (chunk: %2d)\n", alf_tell(f),
           f->current_chunk);

    if ((nbytes = alf_read(buf, 1, strlen(line), f)) != (int)strlen(line)) {
        fprint0(STDERR, "** read after seek failed.\n");
        return 1;
    }

    buf[nbytes] = '\0';
    if (strncmp(line, buf, strlen(line)) != 0) {
        fprint2(STDERR, "** read after seek got wrong data: %s != %s\n",
                buf, line);
        return 1;
    }

    for (i=0; i<10; i++) {
        for (c=0; c<128; c+=2)
            if (alf_putc(c, f) == EOF) {
                fprint1(STDERR, "** alf_putc of %c failed.\n", c);
                return 1;
            }
    }

    alf_close(f);

    /****************************************************/

    print1("%2d. testing append mode.\n", ++test);

    f = alf_open(file1, "a", 0);
    if (f == NULL) {
        fprint1(STDERR, "** open for append failed: %d.\n", errno);
        return 1;
    }
    nbytes = alf_write(line, 1, strlen(line), f);
    print1("    wrote: %2d bytes\n", nbytes);
    if (alf_seek(f, LONG32_MAX+(LONG_LONG)5000, SEEK_SET) == -1) {
            fprint1(STDERR, "** seek to offset %" L "d failed.\n",
                    LONG32_MAX+(LONG_LONG)5000);
            return 1;
    }
    print2("    seeked to position %" L "d (chunk: %2d)\n", alf_tell(f),
           f->current_chunk);
    nbytes = alf_write(line, 1, strlen(line), f);
    print1("    wrote: %2d bytes\n", nbytes);
    if (alf_seek(f, LONG32_MAX-(LONG_LONG)5000, SEEK_SET) == -1) {
            fprint1(STDERR, "** seek to offset %" L "d failed.\n",
                    LONG32_MAX-(LONG_LONG)5000);
            return 1;
    }
    print2("    seeked to position %" L "d (chunk: %2d)\n", alf_tell(f),
           f->current_chunk);
    for (i=0; i<10; i++) {
        for (c=0; c<128; c+=2)
            if (alf_putc(c, f) == EOF) {
                fprint1(STDERR, "** alf_putc of %c failed.\n", c);
                return 1;
            }
    }
    alf_close(f);

    /****************************************************/

#ifndef MS_WIN32
    print1("%2d. testing open of non-ALF file for read.\n", ++test);

    f = alf_open("/etc/hosts", "r", 0);
    if (f != NULL) {
            alf_close(f);
            fprint0(STDERR,
                    "** open of /etc/hosts should not have succeeded.\n");
            return 1;
    }

    /****************************************************/

    print1("%2d. testing open of read-only ALF file.\n", ++test);

    chmod(file1, 0555);
    f = alf_open(file1, "r", 0);
    if (f == NULL) {
        fprint1(STDERR, "** open for read of read-only file failed: %d.\n",
                errno);
        return 1;
    }
    else {
        nbytes = alf_read(buf, 1, 10, f);
        buf[nbytes] = '\0';
        print1("    read: %2d bytes\n", nbytes);
        alf_rewind(f);
        if ((int)alf_read(buf2, 1, 10, f) != nbytes) {
            fprint0(STDERR, "** read after rewind not the same.\n");
            return 1;
        }
        else {
            for (i=0; i<10; i++) {
                if (buf[i] != buf2[i]) {
                    fprint0(STDERR, "** read after rewind not the same.\n");
                    return 1;
                }
            }
        }
        print1("    truncating from %lld to 10\n", f->length);
        if (alf_truncate(f, 10) != -1) {
            fprint0(STDERR,
                    "** truncate of read-only file succeeded spuriously.\n");
            return 1;
        }
        alf_close(f);
    }

    /****************************************************/

    print1("%2d. testing append mode open of read-only ALF file.\n", ++test);

    f = alf_open(file1, "a", 0);
    if (f != NULL) {
            alf_close(f);
            fprint0(STDERR, "** open for append of read-only file"
                    " should not have succeeded.\n");
            return 1;
    }

    /****************************************************/

    print1("%2d. testing open of read-protected ALF file.\n", ++test);

    chmod(file1, 0055);
    f = alf_open(file1, "r", 0);
    if (f != NULL) {
            alf_close(f);
            fprint0(STDERR, "open for read of read-protected file"
                    " should not have succeeded.\n");
            return 1;
    }
    chmod(file1, 0755);

    /****************************************************/

    print1("%2d. testing alf_reopen.\n", ++test);

    f = alf_open(file1, "r", 0);
    if (f == NULL) {
        fprint1(STDERR, "** open for write failed: %d.\n", errno);
        return 1;
    }
    f = alf_reopen(file2, "w", 0, f);
    if (f == NULL) {
        fprint1(STDERR, "** reopen from foo to foo2 failed: %d.\n", errno);
        return 1;
    }
    else
        alf_close(f);

    /****************************************************/

    print1("%2d. testing long seeks, truncation, and alf_puts.\n", ++test);

    f = alf_open(file1, "w", 0);
    if (f == NULL) {
        fprint1(STDERR, "** open for write failed: %d.\n", errno);
        return 1;
    }
    for (i=0; i<10; i++) {
        if (alf_write("abc", 1, 3, f) != 3) {
            fprint1(STDERR, "** write failed: %d.\n", errno);
            return 1;
        }
        if (alf_seek(f, (i+1)*(LONG_LONG)LONG32_MAX, SEEK_SET) == -1) {
            fprint0(STDERR, "** seek failed.\n");
            return 1;
        }
    }
    
    print1("    truncating from %lld to 3000\n", f->length);
    if (alf_truncate(f, 3000) == -1) {
        fprint0(STDERR, "** truncate failed.\n");
        return 1;
    }
    alf_puts("abc", f);
    alf_puts("", f);
    alf_close(f);

    /****************************************************/

    print1("%2d. testing write mode on existing file\n", ++test);

    f = alf_open(file1, "w", 0);
    if (f == NULL) {
        fprint1(STDERR, "** open for write failed: %d.\n", errno);
        return 1;
    }
    if (alf_write("1234567890", 1, 10, f) != 10) {
        fprint0(STDERR, "** create/write failed.\n");
        return 1;
    }
    alf_close(f);

    /****************************************************/

    print1("%2d. testing append mode on existing file\n", ++test);

    f = alf_open(file1, "a", 0);
    if (f == NULL) {
        fprint1(STDERR, "** open for append failed: %d.\n", errno);
        return 1;
    }
    if (alf_seek(f, 3, SEEK_SET) == -1) {
        fprint0(STDERR, "** seek failed.\n");
        return 1;
    }
    if (alf_write("abc", 1, 3, f) != 3) {
        fprint0(STDERR, "** write in append mode failed.\n");
        return 1;
    }
    alf_close(f);

    print1("%2d. reading back bytes written during append mode\n", ++test);

    f = alf_open(file1, "r", 0);
    if (f == NULL) {
        fprint1(STDERR, "** open for read failed: %d.\n", errno);
        return 1;
    }
    nbytes = alf_read(buf, 1, DIM(buf)-1, f);
    if (nbytes != 13) {
        fprint0(STDERR, "** failed to read all 13 bytes from file.\n");
        return 1;
    }
    alf_close(f);
    buf[nbytes] = '\0';
    if (strcmp("1234567890abc", buf) != 0) {
        fprint1(STDERR, "** failed to get expected contents: %s\n", buf);
        return 1;
    }

    /****************************************************/

    print1("%2d. testing alf_flush, read across multiple chunks, "
           "read of writable file\n", ++test);

    f = alf_open(file1, "w", 0);
    if (f == NULL) {
        fprint0(STDERR, "** create failed.\n");
        return 1;
    }

    if (alf_read(buf, 1, 1, f) != 0) {
        fprint0(STDERR, "** read in write mode succeeded spuriously.\n");
        return 1;
    }
    alf_clearerr(f);

    if (alf_write("123\0", 1, 4, f) != 4) {
        fprint0(STDERR, "** write failed.\n");
        return 1;
    }
    if (alf_seek(f, 10, SEEK_SET) == -1) {
        fprint0(STDERR, "** seek failed.\n");
        return 1;
    }
    if (alf_write("456\0", 1, 4, f) != 4) {
        fprint0(STDERR, "** write failed.\n");
        return 1;
    }
    if (alf_seek(f, 20, SEEK_SET) == -1) {
        fprint0(STDERR, "** seek failed.\n");
        return 1;
    }
    if (alf_write("789\0", 1, 4, f) != 4) {
        fprint0(STDERR, "** write failed.\n");
        return 1;
    }
    if (alf_seek(f, 30, SEEK_SET) == -1) {
        fprint0(STDERR, "** seek failed.\n");
        return 1;
    }
    if (alf_write("ABC\0", 1, 4, f) != 4) {
        fprint0(STDERR, "** write failed.\n");
        return 1;
    }
    if (alf_flush(f) == -1) {
        fprint0(STDERR, "** flush failed.\n");
        return 1;
    }
    alf_close(f);

    f = alf_open(file1, "r", 0);
    if (f == NULL) {
        fprint1(STDERR, "** open for read failed: %d.\n", errno);
        return 1;
    }

    if (alf_read(buf, 1, 34, f) != 34) {
        fprint0(STDERR, "** read failed.\n");
        return 1;
    }

    if (alf_flush(f) != -1) {
        fprint0(STDERR, "** flush succeeded spuriously.\n");
        return 1;
    }

    if (strcmp(&buf[ 0], "123") != 0 ||
        strcmp(&buf[10], "456") != 0 ||
        strcmp(&buf[20], "789") != 0 ||
        strcmp(&buf[30], "ABC") != 0) {
        fprint0(STDERR, "** read didn't get correct data.\n");
        return 1;
    }

    /****************************************************/

    print1("%2d. testing write on file opened for read\n", ++test);

    f = alf_open(file1, "r", 0);
    if (f == NULL) {
        fprint1(STDERR, "** open for read failed: %d.\n", errno);
        return 1;
    }

    if (alf_write("XXX", 1, 3, f) != 0) {
        fprint0(STDERR, "** write in read mode succeeded spuriously.\n");
        return 1;
    }
    if (alf_error(f) == 0) {
        fprint0(STDERR, "** error flag not set after write error.\n");
        return 1;
    }
    alf_clearerr(f);

    if (alf_putc('d', f) != -1) {
        fprint0(STDERR, "** write in read mode succeeded spuriously.\n");
        return 1;
    }
    alf_clearerr(f);

    alf_close(f);
#endif

    return 0;
}


/*
 * Local Variables: ***
 * c-basic-offset: 4 ***
 * indent-tabs-mode: nil ***
 * End: ***
 */


syntax highlighted by Code2HTML, v. 0.9.1