
/* Copyright (c) 1999-2014 by Digital Mars
 * All Rights Reserved, written by Walter Bright
 * http://www.digitalmars.com
 * Distributed under the Boost Software License, Version 1.0.
 * (See accompanying file LICENSE or copy at http://www.boost.org/LICENSE_1_0.txt)
 * https://github.com/D-Programming-Language/dmd/blob/master/src/root/file.c
 */

#include "file.h"

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

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

#if _WIN32
#include <windows.h>
#include <direct.h>
#include <errno.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 "filename.h"
#include "array.h"
#include "port.h"
#include "rmem.h"

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

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

File *File::create(const char *n)
{
    return new File(n);
}

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

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

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

bool File::read()
{
    if (len)
        return false;               // already read the file
#if POSIX
    size_t size;
    struct stat buf;
    ssize_t numread;

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

    if (!ref)
        ::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 = (size_t)buf.st_size;
    buffer = (unsigned char *) ::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 (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 false;

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

err1:
    return true;
#elif _WIN32
    DWORD size;
    DWORD numread;

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

    if (!ref)
        ::free(buffer);
    ref = 0;

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

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

    if (numread != size)
        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:
    ::free(buffer);
    buffer = NULL;
    len = 0;

err1:
    return true;
#else
    assert(0);
#endif
}

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

bool File::write()
{
#if POSIX
    ssize_t numwritten;

    char *name = this->name->toChars();
    int fd = open(name, O_CREAT | O_WRONLY | O_TRUNC, (6 << 6) | (4 << 3) | 4);
    if (fd == -1)
        goto err;

    numwritten = ::write(fd, buffer, len);
    if (len != numwritten)
        goto err2;

    if (close(fd) == -1)
        goto err;

    return false;

err2:
    close(fd);
    ::remove(name);
err:
    return true;
#elif _WIN32
    DWORD numwritten;

    char *name = this->name->toChars();
    HANDLE 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 (!CloseHandle(h))
        goto err;
    return false;

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

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

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