
// Copyright (c) 1999-2009 by Digital Mars
// All Rights Reserved
// written by Walter Bright
// www.digitalmars.com
// License for redistribution is by either the Artistic License
// in artistic.txt, or the GNU General Public License in gnu.txt.
// See the included readme.txt for details.

#ifndef POSIX
#define POSIX (linux || __APPLE__ || __FreeBSD__ || __sun&&__SVR4)
#endif

#include <stdio.h>
#include <stdlib.h>
#include <stdarg.h>
#include <string.h>
#include <stdint.h>
#include <assert.h>

#if (defined (__SVR4) && defined (__sun))
#include <alloca.h>
#endif

#if _MSC_VER ||__MINGW32__
#include <malloc.h>
#include <string>
#endif

#if _WIN32
#include <windows.h>
#include <direct.h>
#endif

#if POSIX
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <errno.h>
#include <unistd.h>
#include <utime.h>
#endif

#include "port.h"
#include "root.h"
#include "dchar.h"
#include "rmem.h"

#if 0 //__SC__ //def DEBUG
extern "C" void __cdecl _assert(void *e, void *f, unsigned line)
{
    printf("Assert('%s','%s',%d)\n",e,f,line);
    fflush(stdout);
    *(char *)0 = 0;
}
#endif


/*************************************
 * Convert wchar string to ascii string.
 */

char *wchar2ascii(wchar_t *us)
{
    return wchar2ascii(us, wcslen(us));
}

char *wchar2ascii(wchar_t *us, unsigned len)
{
    unsigned i;
    char *p;

    p = (char *)mem.malloc(len + 1);
    for (i = 0; i <= len; i++)
	p[i] = (char) us[i];
    return p;
}

int wcharIsAscii(wchar_t *us)
{
    return wcharIsAscii(us, wcslen(us));
}

int wcharIsAscii(wchar_t *us, unsigned len)
{
    unsigned i;

    for (i = 0; i <= len; i++)
    {
	if (us[i] & ~0xFF)	// if high bits set
	    return 0;		// it's not ascii
    }
    return 1;
}


/***********************************
 * Compare length-prefixed strings (bstr).
 */

int bstrcmp(unsigned char *b1, unsigned char *b2)
{
    return (*b1 == *b2 && memcmp(b1 + 1, b2 + 1, *b2) == 0) ? 0 : 1;
}

/***************************************
 * Convert bstr into a malloc'd string.
 */

char *bstr2str(unsigned char *b)
{
    char *s;
    unsigned len;

    len = *b;
    s = (char *) mem.malloc(len + 1);
    s[len] = 0;
    return (char *)memcpy(s,b + 1,len);
}

/**************************************
 * Print error message and exit.
 */

void error(const char *format, ...)
{
    va_list ap;

    va_start(ap, format);
    printf("Error: ");
    vprintf(format, ap);
    va_end( ap );
    printf("\n");
    fflush(stdout);

    exit(EXIT_FAILURE);
}

#if M_UNICODE
void error(const dchar *format, ...)
{
    va_list ap;

    va_start(ap, format);
    printf("Error: ");
    vwprintf(format, ap);
    va_end( ap );
    printf("\n");
    fflush(stdout);

    exit(EXIT_FAILURE);
}
#endif

void error_mem()
{
    error("out of memory");
}

/**************************************
 * Print warning message.
 */

void warning(const char *format, ...)
{
    va_list ap;

    va_start(ap, format);
    printf("Warning: ");
    vprintf(format, ap);
    va_end( ap );
    printf("\n");
    fflush(stdout);
}

/****************************** Object ********************************/

int Object::equals(Object *o)
{
    return o == this;
}

hash_t Object::hashCode()
{
    return (hash_t) this;
}

int Object::compare(Object *obj)
{
    return this - obj;
}

void Object::print()
{
    printf("%s %p\n", toChars(), this);
}

char *Object::toChars()
{
    return (char *)"Object";
}

dchar *Object::toDchars()
{
#if M_UNICODE
    return L"Object";
#else
    return toChars();
#endif
}

int Object::dyncast()
{
    return 0;
}

void Object::toBuffer(OutBuffer *b)
{
    b->writestring("Object");
}

void Object::mark()
{
}

/****************************** String ********************************/

String::String(char *str, int ref)
{
    this->str = ref ? str : mem.strdup(str);
    this->ref = ref;
}

String::~String()
{
    mem.free(str);
}

void String::mark()
{
    mem.mark(str);
}

hash_t String::calcHash(const char *str, size_t len)
{
    hash_t hash = 0;

    for (;;)
    {
	switch (len)
	{
	    case 0:
		return hash;

	    case 1:
		hash *= 37;
		hash += *(uint8_t *)str;
		return hash;

	    case 2:
		hash *= 37;
		hash += *(uint16_t *)str;
		return hash;

	    case 3:
		hash *= 37;
		hash += (*(uint16_t *)str << 8) +
			((uint8_t *)str)[2];
		return hash;

	    default:
		hash *= 37;
		hash += *(uint32_t *)str;
		str += 4;
		len -= 4;
		break;
	}
    }
}

hash_t String::calcHash(const char *str)
{
    return calcHash(str, strlen(str));
}

hash_t String::hashCode()
{
    return calcHash(str, strlen(str));
}

unsigned String::len()
{
    return strlen(str);
}

int String::equals(Object *obj)
{
    return strcmp(str,((String *)obj)->str) == 0;
}

int String::compare(Object *obj)
{
    return strcmp(str,((String *)obj)->str);
}

char *String::toChars()
{
    return str;
}

void String::print()
{
    printf("String '%s'\n",str);
}


/****************************** FileName ********************************/

FileName::FileName(char *str, int ref)
    : String(str,ref)
{
}

char *FileName::combine(const char *path, const char *name)
{   char *f;
    size_t pathlen;
    size_t namelen;

    if (!path || !*path)
	return (char *)name;
    pathlen = strlen(path);
    namelen = strlen(name);
    f = (char *)mem.malloc(pathlen + 1 + namelen + 1);
    memcpy(f, path, pathlen);
#if POSIX
    if (path[pathlen - 1] != '/')
    {	f[pathlen] = '/';
	pathlen++;
    }
#elif _WIN32
    if (path[pathlen - 1] != '\\' &&
	path[pathlen - 1] != '/'  &&
	path[pathlen - 1] != ':')
    {	f[pathlen] = '\\';
	pathlen++;
    }
#else
    assert(0);
#endif
    memcpy(f + pathlen, name, namelen + 1);
    return f;
}

FileName::FileName(char *path, char *name)
    : String(combine(path,name),1)
{
}

// Split a path into an Array of paths
Array *FileName::splitPath(const char *path)
{
    char c = 0;				// unnecessary initializer is for VC /W4
    const char *p;
    OutBuffer buf;
    Array *array;

    array = new Array();
    if (path)
    {
	p = path;
	do
	{   char instring = 0;

	    while (isspace(*p))		// skip leading whitespace
		p++;
	    buf.reserve(strlen(p) + 1);	// guess size of path
	    for (; ; p++)
	    {
		c = *p;
		switch (c)
		{
		    case '"':
			instring ^= 1;	// toggle inside/outside of string
			continue;

#if MACINTOSH
		    case ',':
#endif
#if _WIN32
		    case ';':
#endif
#if POSIX
		    case ':':
#endif
			p++;
			break;		// note that ; cannot appear as part
					// of a path, quotes won't protect it

		    case 0x1A:		// ^Z means end of file
		    case 0:
			break;

		    case '\r':
			continue;	// ignore carriage returns

#if POSIX
		    case '~':
			buf.writestring(getenv("HOME"));
			continue;
#endif

#if 0
		    case ' ':
		    case '\t':		// tabs in filenames?
			if (!instring)	// if not in string
			    break;	// treat as end of path
#endif
		    default:
			buf.writeByte(c);
			continue;
		}
		break;
	    }
	    if (buf.offset)		// if path is not empty
	    {
		buf.writeByte(0);	// to asciiz
		array->push(buf.extractData());
	    }
	} while (c);
    }
    return array;
}

hash_t FileName::hashCode()
{
#if _WIN32
    // We need a different hashCode because it must be case-insensitive
    size_t len = strlen(str);
    hash_t hash = 0;
    unsigned char *s = (unsigned char *)str;

    for (;;)
    {
	switch (len)
	{
	    case 0:
		return hash;

	    case 1:
		hash *= 37;
		hash += *(uint8_t *)s | 0x20;
		return hash;

	    case 2:
		hash *= 37;
		hash += *(uint16_t *)s | 0x2020;
		return hash;

	    case 3:
		hash *= 37;
		hash += ((*(uint16_t *)s << 8) +
			 ((uint8_t *)s)[2]) | 0x202020;
		break;

	    default:
		hash *= 37;
		hash += *(uint32_t *)s | 0x20202020;
		s += 4;
		len -= 4;
		break;
	}
    }
#else
    // darwin HFS is case insensitive, though...
    return String::hashCode();
#endif
}

int FileName::compare(Object *obj)
{
    return compare(str, ((FileName *)obj)->str);
}

int FileName::compare(const char *name1, const char *name2)
{
#if _WIN32
    return stricmp(name1, name2);
#else
    return strcmp(name1, name2);
#endif
}

int FileName::equals(Object *obj)
{
    return compare(obj) == 0;
}

int FileName::equals(const char *name1, const char *name2)
{
    return compare(name1, name2) == 0;
}

/************************************
 * Return !=0 if absolute path name.
 */

int FileName::absolute(const char *name)
{
#if _WIN32
    return (*name == '\\') ||
	   (*name == '/')  ||
	   (*name && name[1] == ':');
#elif POSIX
    return (*name == '/');
#else
    assert(0);
#endif
}

/********************************
 * Return filename extension (read-only).
 * Points past '.' of extension.
 * If there isn't one, return NULL.
 */

char *FileName::ext(const char *str)
{
    char *e;
    size_t len = strlen(str);

    e = (char *)str + len;
    for (;;)
    {
	switch (*e)
	{   case '.':
		return e + 1;
#if POSIX
	    case '/':
	        break;
#endif
#if _WIN32
	    case '\\':
	    case ':':
	    case '/':
		break;
#endif
	    default:
		if (e == str)
		    break;
		e--;
		continue;
	}
	return NULL;
    }
}

char *FileName::ext()
{
    return ext(str);
}

/********************************
 * Return mem.malloc'd filename with extension removed.
 */

char *FileName::removeExt(const char *str)
{
    const char *e = ext(str);
    if (e)
    {	size_t len = (e - str) - 1;
	char *n = (char *)mem.malloc(len + 1);
	memcpy(n, str, len);
	n[len] = 0;
	return n;
    }
    return mem.strdup(str);
}

/********************************
 * Return filename name excluding path (read-only).
 */

char *FileName::name(const char *str)
{
    char *e;
    size_t len = strlen(str);

    e = (char *)str + len;
    for (;;)
    {
	switch (*e)
	{
#if POSIX
	    case '/':
	       return e + 1;
#endif
#if _WIN32
	    case '/':
	    case '\\':
		return e + 1;
	    case ':':
		/* The ':' is a drive letter only if it is the second
		 * character or the last character,
		 * otherwise it is an ADS (Alternate Data Stream) separator.
		 * Consider ADS separators as part of the file name.
		 */
		if (e == str + 1 || e == str + len - 1)
		    return e + 1;
#endif
	    default:
		if (e == str)
		    break;
		e--;
		continue;
	}
	return e;
    }
}

char *FileName::name()
{
    return name(str);
}

/**************************************
 * Return path portion of str.
 * Path will does not include trailing path separator.
 */

char *FileName::path(const char *str)
{
    char *n = name(str);
    char *path;
    size_t pathlen;

    if (n > str)
    {
#if POSIX
	if (n[-1] == '/')
	    n--;
#elif _WIN32
	if (n[-1] == '\\' || n[-1] == '/')
	    n--;
#else
	assert(0);
#endif
    }
    pathlen = n - str;
    path = (char *)mem.malloc(pathlen + 1);
    memcpy(path, str, pathlen);
    path[pathlen] = 0;
    return path;
}

/**************************************
 * Replace filename portion of path.
 */

const char *FileName::replaceName(const char *path, const char *name)
{   char *f;
    char *n;
    size_t pathlen;
    size_t namelen;

    if (absolute(name))
	return name;

    n = FileName::name(path);
    if (n == path)
	return name;
    pathlen = n - path;
    namelen = strlen(name);
    f = (char *)mem.malloc(pathlen + 1 + namelen + 1);
    memcpy(f, path, pathlen);
#if POSIX
    if (path[pathlen - 1] != '/')
    {	f[pathlen] = '/';
	pathlen++;
    }
#elif _WIN32
    if (path[pathlen - 1] != '\\' &&
	path[pathlen - 1] != '/' &&
	path[pathlen - 1] != ':')
    {	f[pathlen] = '\\';
	pathlen++;
    }
#else
    assert(0);
#endif
    memcpy(f + pathlen, name, namelen + 1);
    return f;
}

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

FileName *FileName::defaultExt(const char *name, const char *ext)
{
    char *e;
    char *s;
    size_t len;
    size_t extlen;

    e = FileName::ext(name);
    if (e)				// if already has an extension
	return new FileName((char *)name, 0);

    len = strlen(name);
    extlen = strlen(ext);
    s = (char *)alloca(len + 1 + extlen + 1);
    memcpy(s,name,len);
    s[len] = '.';
    memcpy(s + len + 1, ext, extlen + 1);
    return new FileName(s, 0);
}

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

FileName *FileName::forceExt(const char *name, const char *ext)
{
    char *e;
    char *s;
    size_t len;
    size_t extlen;

    e = FileName::ext(name);
    if (e)				// if already has an extension
    {
	len = e - name;
	extlen = strlen(ext);

	s = (char *)alloca(len + extlen + 1);
	memcpy(s,name,len);
	memcpy(s + len, ext, extlen + 1);
	return new FileName(s, 0);
    }
    else
	return defaultExt(name, ext);	// doesn't have one
}

/******************************
 * Return !=0 if extensions match.
 */

int FileName::equalsExt(const char *ext)
{   const char *e;

    e = FileName::ext();
    if (!e && !ext)
	return 1;
    if (!e || !ext)
	return 0;
#if POSIX
    return strcmp(e,ext) == 0;
#elif _WIN32
    return stricmp(e,ext) == 0;
#else
    assert(0);
#endif
}

/*************************************
 * Copy file from this to to.
 */

void FileName::CopyTo(FileName *to)
{
    File file(this);

#if _WIN32
    file.touchtime = mem.malloc(sizeof(WIN32_FIND_DATAA));	// keep same file time
#elif POSIX
    file.touchtime = mem.malloc(sizeof(struct stat)); // keep same file time
#else
    assert(0);
#endif
    file.readv();
    file.name = to;
    file.writev();
}

/*************************************
 * Search Path for file.
 * Input:
 *	cwd	if !=0, search current directory before searching path
 */

char *FileName::searchPath(Array *path, const char *name, int cwd)
{
    if (absolute(name))
    {
	return exists(name) ? (char *)name : NULL;
    }
    if (cwd)
    {
	if (exists(name))
	    return (char *)name;
    }
    if (path)
    {	unsigned i;

	for (i = 0; i < path->dim; i++)
	{
	    char *p = (char *)path->data[i];
	    char *n = combine(p, name);

	    if (exists(n))
		return n;
	}
    }
    return NULL;
}

int FileName::exists(const char *name)
{
#if POSIX
    struct stat st;

    if (stat(name, &st) < 0)
	return 0;
    if (S_ISDIR(st.st_mode))
	return 2;
    return 1;
#elif _WIN32
    DWORD dw;
    int result;

    dw = GetFileAttributesA(name);
    if (dw == -1L)
	result = 0;
    else if (dw & FILE_ATTRIBUTE_DIRECTORY)
	result = 2;
    else
	result = 1;
    return result;
#else
    assert(0);
#endif
}

void FileName::ensurePathExists(const char *path)
{
    //printf("FileName::ensurePathExists(%s)\n", path ? path : "");
    if (path && *path)
    {
	if (!exists(path))
	{
	    char *p = FileName::path(path);
	    if (*p)
	    {
#if _WIN32
		size_t len = strlen(path);
		if (len > 2 && p[-1] == ':' && path + 2 == p)
		{   mem.free(p);
		    return;
		}
#endif
		ensurePathExists(p);
		mem.free(p);
	    }
#if _WIN32
	    if (path[strlen(path) - 1] != '\\')
#endif
#if POSIX
	    if (path[strlen(path) - 1] != '\\')
#endif
	    {
		//printf("mkdir(%s)\n", path);
#if _WIN32
		if (mkdir(path))
#endif
#if POSIX
		if (mkdir(path, 0777))
#endif
		    error("cannot create directory %s", path);
	    }
	}
    }
}

/****************************** File ********************************/

File::File(FileName *n)
{
    ref = 0;
    buffer = NULL;
    len = 0;
    touchtime = NULL;
    name = n;
}

File::File(char *n)
{
    ref = 0;
    buffer = NULL;
    len = 0;
    touchtime = NULL;
    name = new FileName(n, 0);
}

File::~File()
{
    if (buffer)
    {
	if (ref == 0)
	    mem.free(buffer);
#if _WIN32
	else if (ref == 2)
	    UnmapViewOfFile(buffer);
#endif
    }
    if (touchtime)
	mem.free(touchtime);
}

void File::mark()
{
    mem.mark(buffer);
    mem.mark(touchtime);
    mem.mark(name);
}

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

int File::read()
{
#if POSIX
    off_t size;
    ssize_t numread;
    int fd;
    struct stat buf;
    int result = 0;
    char *name;

    name = this->name->toChars();
    //printf("File::read('%s')\n",name);
    fd = open(name, O_RDONLY);
    if (fd == -1)
    {	result = errno;
	//printf("\topen error, errno = %d\n",errno);
	goto err1;
    }

    if (!ref)
	mem.free(buffer);
    ref = 0;       // we own the buffer now

    //printf("\tfile opened\n");
    if (fstat(fd, &buf))
    {
	printf("\tfstat error, errno = %d\n",errno);
        goto err2;
    }
    size = buf.st_size;
    buffer = (unsigned char *) mem.malloc(size + 2);
    if (!buffer)
    {
	printf("\tmalloc error, errno = %d\n",errno);
	goto err2;
    }

    numread = ::read(fd, buffer, size);
    if (numread != size)
    {
	printf("\tread error, errno = %d\n",errno);
	goto err2;
    }

    if (touchtime)
        memcpy(touchtime, &buf, sizeof(buf));

    if (close(fd) == -1)
    {
	printf("\tclose error, errno = %d\n",errno);
	goto err;
    }

    len = size;

    // Always store a wchar ^Z past end of buffer so scanner has a sentinel
    buffer[size] = 0;		// ^Z is obsolete, use 0
    buffer[size + 1] = 0;
    return 0;

err2:
    close(fd);
err:
    mem.free(buffer);
    buffer = NULL;
    len = 0;

err1:
    result = 1;
    return result;
#elif _WIN32
    DWORD size;
    DWORD numread;
    HANDLE h;
    int result = 0;
    char *name;

    name = this->name->toChars();
    h = CreateFileA(name,GENERIC_READ,FILE_SHARE_READ,NULL,OPEN_EXISTING,
	FILE_ATTRIBUTE_NORMAL | FILE_FLAG_SEQUENTIAL_SCAN,0);
    if (h == INVALID_HANDLE_VALUE)
	goto err1;

    if (!ref)
	mem.free(buffer);
    ref = 0;

    size = GetFileSize(h,NULL);
    buffer = (unsigned char *) mem.malloc(size + 2);
    if (!buffer)
	goto err2;

    if (ReadFile(h,buffer,size,&numread,NULL) != TRUE)
	goto err2;

    if (numread != size)
	goto err2;

    if (touchtime)
    {
	if (!GetFileTime(h, NULL, NULL, &((WIN32_FIND_DATAA *)touchtime)->ftLastWriteTime))
	    goto err2;
    }

    if (!CloseHandle(h))
	goto err;

    len = size;

    // Always store a wchar ^Z past end of buffer so scanner has a sentinel
    buffer[size] = 0;		// ^Z is obsolete, use 0
    buffer[size + 1] = 0;
    return 0;

err2:
    CloseHandle(h);
err:
    mem.free(buffer);
    buffer = NULL;
    len = 0;

err1:
    result = 1;
    return result;
#else
    assert(0);
#endif
}

/*****************************
 * Read a file with memory mapped file I/O.
 */

int File::mmread()
{
#if POSIX
    return read();
#elif _WIN32
    HANDLE hFile;
    HANDLE hFileMap;
    DWORD size;
    char *name;

    name = this->name->toChars();
    hFile = CreateFile(name, GENERIC_READ,
			FILE_SHARE_READ, NULL,
			OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
    if (hFile == INVALID_HANDLE_VALUE)
	goto Lerr;
    size = GetFileSize(hFile, NULL);
    //printf(" file created, size %d\n", size);

    hFileMap = CreateFileMapping(hFile,NULL,PAGE_READONLY,0,size,NULL);
    if (CloseHandle(hFile) != TRUE)
	goto Lerr;

    if (hFileMap == NULL)
	goto Lerr;

    //printf(" mapping created\n");

    if (!ref)
	mem.free(buffer);
    ref = 2;
    buffer = (unsigned char *)MapViewOfFileEx(hFileMap, FILE_MAP_READ,0,0,size,NULL);
    if (CloseHandle(hFileMap) != TRUE)
	goto Lerr;
    if (buffer == NULL)			// mapping view failed
	goto Lerr;

    len = size;
    //printf(" buffer = %p\n", buffer);

    return 0;

Lerr:
    return GetLastError();			// failure
#else
    assert(0);
#endif
}

/*********************************************
 * Write a file.
 * Returns:
 *	0	success
 */

int File::write()
{
#if POSIX
    int fd;
    ssize_t numwritten;
    char *name;

    name = this->name->toChars();
    fd = open(name, O_CREAT | O_WRONLY | O_TRUNC, 0644);
    if (fd == -1)
	goto err;

    numwritten = ::write(fd, buffer, len);
    if (len != numwritten)
	goto err2;
    
    if (close(fd) == -1)
	goto err;

    if (touchtime)
    {   struct utimbuf ubuf;

        ubuf.actime = ((struct stat *)touchtime)->st_atime;
        ubuf.modtime = ((struct stat *)touchtime)->st_mtime;
	if (utime(name, &ubuf))
	    goto err;
    }
    return 0;

err2:
    close(fd);
    ::remove(name);
err:
    return 1;
#elif _WIN32
    HANDLE h;
    DWORD numwritten;
    char *name;

    name = this->name->toChars();
    h = CreateFileA(name,GENERIC_WRITE,0,NULL,CREATE_ALWAYS,
	FILE_ATTRIBUTE_NORMAL | FILE_FLAG_SEQUENTIAL_SCAN,NULL);
    if (h == INVALID_HANDLE_VALUE)
	goto err;

    if (WriteFile(h,buffer,len,&numwritten,NULL) != TRUE)
	goto err2;

    if (len != numwritten)
	goto err2;
    
    if (touchtime) {
        SetFileTime(h, NULL, NULL, &((WIN32_FIND_DATAA *)touchtime)->ftLastWriteTime);
    }
    if (!CloseHandle(h))
	goto err;
    return 0;

err2:
    CloseHandle(h);
    DeleteFileA(name);
err:
    return 1;
#else
    assert(0);
#endif
}

/*********************************************
 * Append to a file.
 * Returns:
 *	0	success
 */

int File::append()
{
#if POSIX
    return 1;
#elif _WIN32
    HANDLE h;
    DWORD numwritten;
    char *name;

    name = this->name->toChars();
    h = CreateFileA(name,GENERIC_WRITE,0,NULL,OPEN_ALWAYS,
	FILE_ATTRIBUTE_NORMAL | FILE_FLAG_SEQUENTIAL_SCAN,NULL);
    if (h == INVALID_HANDLE_VALUE)
	goto err;

#if 1
    SetFilePointer(h, 0, NULL, FILE_END);
#else // INVALID_SET_FILE_POINTER doesn't seem to have a definition
    if (SetFilePointer(h, 0, NULL, FILE_END) == INVALID_SET_FILE_POINTER)
	goto err;
#endif

    if (WriteFile(h,buffer,len,&numwritten,NULL) != TRUE)
	goto err2;

    if (len != numwritten)
	goto err2;
    
    if (touchtime) {
        SetFileTime(h, NULL, NULL, &((WIN32_FIND_DATAA *)touchtime)->ftLastWriteTime);
    }
    if (!CloseHandle(h))
	goto err;
    return 0;

err2:
    CloseHandle(h);
err:
    return 1;
#else
    assert(0);
#endif
}

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

void File::readv()
{
    if (read())
	error("Error reading file '%s'\n",name->toChars());
}

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

void File::mmreadv()
{
    if (mmread())
	readv();
}

void File::writev()
{
    if (write())
	error("Error writing file '%s'\n",name->toChars());
}

void File::appendv()
{
    if (write())
	error("Error appending to file '%s'\n",name->toChars());
}

/*******************************************
 * Return !=0 if file exists.
 *	0:	file doesn't exist
 *	1:	normal file
 *	2:	directory
 */

int File::exists()
{
#if POSIX
    return 0;
#elif _WIN32
    DWORD dw;
    int result;
    char *name;

    name = this->name->toChars();
    if (touchtime)
	dw = ((WIN32_FIND_DATAA *)touchtime)->dwFileAttributes;
    else
	dw = GetFileAttributesA(name);
    if (dw == -1L)
	result = 0;
    else if (dw & FILE_ATTRIBUTE_DIRECTORY)
	result = 2;
    else
	result = 1;
    return result;
#else
    assert(0);
#endif
}

void File::remove()
{
#if POSIX
    ::remove(this->name->toChars());
#elif _WIN32
    DeleteFileA(this->name->toChars());
#else
    assert(0);
#endif
}

Array *File::match(char *n)
{
    return match(new FileName(n, 0));
}

Array *File::match(FileName *n)
{
#if POSIX
    return NULL;
#elif _WIN32
    HANDLE h;
    WIN32_FIND_DATAA fileinfo;
    Array *a;
    char *c;
    char *name;

    a = new Array();
    c = n->toChars();
    name = n->name();
    h = FindFirstFileA(c,&fileinfo);
    if (h != INVALID_HANDLE_VALUE)
    {
	do
	{
	    // Glue path together with name
	    char *fn;
	    File *f;

	    fn = (char *)mem.malloc(name - c + strlen(fileinfo.cFileName) + 1);
	    memcpy(fn, c, name - c);
	    strcpy(fn + (name - c), fileinfo.cFileName);
	    f = new File(fn);
	    f->touchtime = mem.malloc(sizeof(WIN32_FIND_DATAA));
	    memcpy(f->touchtime, &fileinfo, sizeof(fileinfo));
	    a->push(f);
	} while (FindNextFileA(h,&fileinfo) != FALSE);
	FindClose(h);
    }
    return a;
#else
    assert(0);
#endif
}

int File::compareTime(File *f)
{
#if POSIX
    return 0;
#elif _WIN32
    if (!touchtime)
	stat();
    if (!f->touchtime)
	f->stat();
    return CompareFileTime(&((WIN32_FIND_DATAA *)touchtime)->ftLastWriteTime, &((WIN32_FIND_DATAA *)f->touchtime)->ftLastWriteTime);
#else
    assert(0);
#endif
}

void File::stat()
{
#if POSIX
    if (!touchtime)
    {
	touchtime = mem.calloc(1, sizeof(struct stat));
    }
#elif _WIN32
    HANDLE h;

    if (!touchtime)
    {
	touchtime = mem.calloc(1, sizeof(WIN32_FIND_DATAA));
    }
    h = FindFirstFileA(name->toChars(),(WIN32_FIND_DATAA *)touchtime);
    if (h != INVALID_HANDLE_VALUE)
    {
	FindClose(h);
    }
#else
    assert(0);
#endif
}

void File::checkoffset(size_t offset, size_t nbytes)
{
    if (offset > len || offset + nbytes > len)
	error("Corrupt file '%s': offset x%zx off end of file",toChars(),offset);
}

char *File::toChars()
{
    return name->toChars();
}


/************************* OutBuffer *************************/

OutBuffer::OutBuffer()
{
    data = NULL;
    offset = 0;
    size = 0;
}

OutBuffer::~OutBuffer()
{
    mem.free(data);
}

void *OutBuffer::extractData()
{
    void *p;

    p = (void *)data;
    data = NULL;
    offset = 0;
    size = 0;
    return p;
}

void OutBuffer::mark()
{
    mem.mark(data);
}

void OutBuffer::reserve(unsigned nbytes)
{
    //printf("OutBuffer::reserve: size = %d, offset = %d, nbytes = %d\n", size, offset, nbytes);
    if (size - offset < nbytes)
    {
	size = (offset + nbytes) * 2;
	data = (unsigned char *)mem.realloc(data, size);
    }
}

void OutBuffer::reset()
{
    offset = 0;
}

void OutBuffer::setsize(unsigned size)
{
    offset = size;
}

void OutBuffer::write(const void *data, unsigned nbytes)
{
    reserve(nbytes);
    memcpy(this->data + offset, data, nbytes);
    offset += nbytes;
}

void OutBuffer::writebstring(unsigned char *string)
{
    write(string,*string + 1);
}

void OutBuffer::writestring(const char *string)
{
    write(string,strlen(string));
}

void OutBuffer::writedstring(const char *string)
{
#if M_UNICODE
    for (; *string; string++)
    {
	writedchar(*string);
    }
#else
    write(string,strlen(string));
#endif
}

void OutBuffer::writedstring(const wchar_t *string)
{
#if M_UNICODE
    write(string,wcslen(string) * sizeof(wchar_t));
#else
    for (; *string; string++)
    {
	writedchar(*string);
    }
#endif
}

void OutBuffer::prependstring(const char *string)
{   unsigned len;

    len = strlen(string);
    reserve(len);
    memmove(data + len, data, offset);
    memcpy(data, string, len);
    offset += len;
}

void OutBuffer::writenl()
{
#if _WIN32
#if M_UNICODE
    write4(0x000A000D);		// newline is CR,LF on Microsoft OS's
#else
    writeword(0x0A0D);		// newline is CR,LF on Microsoft OS's
#endif
#else
#if M_UNICODE
    writeword('\n');
#else
    writeByte('\n');
#endif
#endif
}

void OutBuffer::writeByte(unsigned b)
{
    reserve(1);
    this->data[offset] = (unsigned char)b;
    offset++;
}

void OutBuffer::writeUTF8(unsigned b)
{
    reserve(6);
    if (b <= 0x7F)
    {
	this->data[offset] = (unsigned char)b;
	offset++;
    }
    else if (b <= 0x7FF)
    {
	this->data[offset + 0] = (unsigned char)((b >> 6) | 0xC0);
	this->data[offset + 1] = (unsigned char)((b & 0x3F) | 0x80);
	offset += 2;
    }
    else if (b <= 0xFFFF)
    {
	this->data[offset + 0] = (unsigned char)((b >> 12) | 0xE0);
	this->data[offset + 1] = (unsigned char)(((b >> 6) & 0x3F) | 0x80);
	this->data[offset + 2] = (unsigned char)((b & 0x3F) | 0x80);
	offset += 3;
    }
    else if (b <= 0x1FFFFF)
    {
	this->data[offset + 0] = (unsigned char)((b >> 18) | 0xF0);
	this->data[offset + 1] = (unsigned char)(((b >> 12) & 0x3F) | 0x80);
	this->data[offset + 2] = (unsigned char)(((b >> 6) & 0x3F) | 0x80);
	this->data[offset + 3] = (unsigned char)((b & 0x3F) | 0x80);
	offset += 4;
    }
    else if (b <= 0x3FFFFFF)
    {
	this->data[offset + 0] = (unsigned char)((b >> 24) | 0xF8);
	this->data[offset + 1] = (unsigned char)(((b >> 18) & 0x3F) | 0x80);
	this->data[offset + 2] = (unsigned char)(((b >> 12) & 0x3F) | 0x80);
	this->data[offset + 3] = (unsigned char)(((b >> 6) & 0x3F) | 0x80);
	this->data[offset + 4] = (unsigned char)((b & 0x3F) | 0x80);
	offset += 5;
    }
    else if (b <= 0x7FFFFFFF)
    {
	this->data[offset + 0] = (unsigned char)((b >> 30) | 0xFC);
	this->data[offset + 1] = (unsigned char)(((b >> 24) & 0x3F) | 0x80);
	this->data[offset + 2] = (unsigned char)(((b >> 18) & 0x3F) | 0x80);
	this->data[offset + 3] = (unsigned char)(((b >> 12) & 0x3F) | 0x80);
	this->data[offset + 4] = (unsigned char)(((b >> 6) & 0x3F) | 0x80);
	this->data[offset + 5] = (unsigned char)((b & 0x3F) | 0x80);
	offset += 6;
    }
    else
	assert(0);
}

void OutBuffer::writedchar(unsigned b)
{
    reserve(Dchar_mbmax * sizeof(dchar));
    offset = (unsigned char *)Dchar::put((dchar *)(this->data + offset), (dchar)b) -
		this->data;
}

void OutBuffer::prependbyte(unsigned b)
{
    reserve(1);
    memmove(data + 1, data, offset);
    data[0] = (unsigned char)b;
    offset++;
}

void OutBuffer::writeword(unsigned w)
{
    reserve(2);
    *(unsigned short *)(this->data + offset) = (unsigned short)w;
    offset += 2;
}

void OutBuffer::writeUTF16(unsigned w)
{
    reserve(4);
    if (w <= 0xFFFF)
    {
	*(unsigned short *)(this->data + offset) = (unsigned short)w;
	offset += 2;
    }
    else if (w <= 0x10FFFF)
    {
	*(unsigned short *)(this->data + offset) = (unsigned short)((w >> 10) + 0xD7C0);
	*(unsigned short *)(this->data + offset + 2) = (unsigned short)((w & 0x3FF) | 0xDC00);
	offset += 4;
    }
    else
	assert(0);
}

void OutBuffer::write4(unsigned w)
{
    reserve(4);
    *(unsigned long *)(this->data + offset) = w;
    offset += 4;
}

void OutBuffer::write(OutBuffer *buf)
{
    if (buf)
    {	reserve(buf->offset);
	memcpy(data + offset, buf->data, buf->offset);
	offset += buf->offset;
    }
}

void OutBuffer::write(Object *obj)
{
    if (obj)
    {
	writestring(obj->toChars());
    }
}

void OutBuffer::fill0(unsigned nbytes)
{
    reserve(nbytes);
    memset(data + offset,0,nbytes);
    offset += nbytes;
}

void OutBuffer::align(unsigned size)
{   unsigned nbytes;

    nbytes = ((offset + size - 1) & ~(size - 1)) - offset;
    fill0(nbytes);
}


////////////////////////////////////////////////////////////////
// The compiler shipped with Visual Studio 2005 (and possible
// other versions) does not support C99 printf format specfiers
// such as %z and %j
#if _MSC_VER
using std::string;
using std::wstring;

template<typename S>
inline void 
search_and_replace(S& str, const S& what, const S& replacement)
{
    assert(!what.empty());
    size_t pos = str.find(what);
    while (pos != S::npos) 
    {
        str.replace(pos, what.size(), replacement);
        pos = str.find(what, pos + replacement.size());
    }
}
#define WORKAROUND_C99_SPECIFIERS_BUG(S,tmp,f) \
    S tmp = f;                                 \
    search_and_replace(fmt, S("%z"), S("%l")); \
    search_and_replace(fmt, S("%j"), S("%i")); \
    f = tmp.c_str();
#else
#define WORKAROUND_C99_SPECIFIERS_BUG(S,tmp,f)
#endif

void OutBuffer::vprintf(const char *format, va_list args)
{
    char buffer[128];
    char *p;
    unsigned psize;
    int count;

    WORKAROUND_C99_SPECIFIERS_BUG(string, fmt, format);

    p = buffer;
    psize = sizeof(buffer);
    for (;;)
    {
#if _WIN32
	count = _vsnprintf(p,psize,format,args);
	if (count != -1)
	    break;
	psize *= 2;
#elif POSIX
        va_list va;
        va_copy(va, args);
/*
  The functions vprintf(), vfprintf(), vsprintf(), vsnprintf()
  are equivalent to the functions printf(), fprintf(), sprintf(),
  snprintf(), respectively, except that they are called with a
  va_list instead of a variable number of arguments. These
  functions do not call the va_end macro. Consequently, the value
  of ap is undefined after the call. The application should call
  va_end(ap) itself afterwards.
 */
	count = vsnprintf(p,psize,format,va);
        va_end(va);
	if (count == -1)
	    psize *= 2;
	else if (count >= psize)
	    psize = count + 1;
	else
	    break;
#else
    assert(0);
#endif
	p = (char *) alloca(psize);	// buffer too small, try again with larger size
    }
    write(p,count);
}

#if M_UNICODE
void OutBuffer::vprintf(const wchar_t *format, va_list args)
{
    dchar buffer[128];
    dchar *p;
    unsigned psize;
    int count;

    WORKAROUND_C99_SPECIFIERS_BUG(wstring, fmt, format);

    p = buffer;
    psize = sizeof(buffer) / sizeof(buffer[0]);
    for (;;)
    {
#if _WIN32
	count = _vsnwprintf(p,psize,format,args);
	if (count != -1)
	    break;
	psize *= 2;
#elif POSIX
        va_list va;
        va_copy(va, args);
	count = vsnwprintf(p,psize,format,va);
        va_end(va); 

	if (count == -1)
	    psize *= 2;
	else if (count >= psize)
	    psize = count + 1;
	else
	    break;
#else
    assert(0);
#endif
	p = (dchar *) alloca(psize * 2);	// buffer too small, try again with larger size
    }
    write(p,count * 2);
}
#endif

void OutBuffer::printf(const char *format, ...)
{
    va_list ap;
    va_start(ap, format);
    vprintf(format,ap);
    va_end(ap);
}

#if M_UNICODE
void OutBuffer::printf(const wchar_t *format, ...)
{
    va_list ap;
    va_start(ap, format);
    vprintf(format,ap);
    va_end(ap);
}
#endif

void OutBuffer::bracket(char left, char right)
{
    reserve(2);
    memmove(data + 1, data, offset);
    data[0] = left;
    data[offset + 1] = right;
    offset += 2;
}

/******************
 * Insert left at i, and right at j.
 * Return index just past right.
 */

unsigned OutBuffer::bracket(unsigned i, const char *left, unsigned j, const char *right)
{
    size_t leftlen = strlen(left);
    size_t rightlen = strlen(right);
    reserve(leftlen + rightlen);
    insert(i, left, leftlen);
    insert(j + leftlen, right, rightlen);
    return j + leftlen + rightlen;
}

void OutBuffer::spread(unsigned offset, unsigned nbytes)
{
    reserve(nbytes);
    memmove(data + offset + nbytes, data + offset,
	this->offset - offset);
    this->offset += nbytes;
}

/****************************************
 * Returns: offset + nbytes
 */

unsigned OutBuffer::insert(unsigned offset, const void *p, unsigned nbytes)
{
    spread(offset, nbytes);
    memmove(data + offset, p, nbytes);
    return offset + nbytes;
}

void OutBuffer::remove(unsigned offset, unsigned nbytes)
{
    memmove(data + offset, data + offset + nbytes, this->offset - (offset + nbytes));
    this->offset -= nbytes;
}

char *OutBuffer::toChars()
{
    writeByte(0);
    return (char *)data;
}

/********************************* Bits ****************************/

Bits::Bits()
{
    data = NULL;
    bitdim = 0;
    allocdim = 0;
}

Bits::~Bits()
{
    mem.free(data);
}

void Bits::mark()
{
    mem.mark(data);
}

void Bits::resize(unsigned bitdim)
{
    unsigned allocdim;
    unsigned mask;

    allocdim = (bitdim + 31) / 32;
    data = (unsigned *)mem.realloc(data, allocdim * sizeof(data[0]));
    if (this->allocdim < allocdim)
	memset(data + this->allocdim, 0, (allocdim - this->allocdim) * sizeof(data[0]));

    // Clear other bits in last word
    mask = (1 << (bitdim & 31)) - 1;
    if (mask)
	data[allocdim - 1] &= ~mask;

    this->bitdim = bitdim;
    this->allocdim = allocdim;
}

void Bits::set(unsigned bitnum)
{
    data[bitnum / 32] |= 1 << (bitnum & 31);
}

void Bits::clear(unsigned bitnum)
{
    data[bitnum / 32] &= ~(1 << (bitnum & 31));
}

int Bits::test(unsigned bitnum)
{
    return data[bitnum / 32] & (1 << (bitnum & 31));
}

void Bits::set()
{   unsigned mask;

    memset(data, ~0, allocdim * sizeof(data[0]));

    // Clear other bits in last word
    mask = (1 << (bitdim & 31)) - 1;
    if (mask)
	data[allocdim - 1] &= mask;
}

void Bits::clear()
{
    memset(data, 0, allocdim * sizeof(data[0]));
}

void Bits::copy(Bits *from)
{
    assert(bitdim == from->bitdim);
    memcpy(data, from->data, allocdim * sizeof(data[0]));
}

Bits *Bits::clone()
{
    Bits *b;

    b = new Bits();
    b->resize(bitdim);
    b->copy(this);
    return b;
}

void Bits::sub(Bits *b)
{
    unsigned u;

    for (u = 0; u < allocdim; u++)
	data[u] &= ~b->data[u];
}















