view dmd2/root/root.c @ 1650:40bd4a0d4870

Update to work with LLVM 2.7. Removed use of dyn_cast, llvm no compiles without exceptions and rtti by default. We do need exceptions for the libconfig stuff, but rtti isn't necessary (anymore). Debug info needs to be rewritten, as in LLVM 2.7 the format has completely changed. To have something to look at while rewriting, the old code has been wrapped inside #ifndef DISABLE_DEBUG_INFO , this means that you have to define this to compile at the moment. Updated tango 0.99.9 patch to include updated EH runtime code, which is needed for LLVM 2.7 as well.
author Tomas Lindquist Olsen
date Wed, 19 May 2010 12:42:32 +0200
parents e4f7b5d9c68a
children
line wrap: on
line source


// 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];
}