#include "../osbs.inl"
#include "../bfile.h"
#include <sewer/bmem.h>
#include <sewer/cassert.h>
#include <sewer/ptr.h>
#include <sewer/unicode.h>
#if !defined(__WINDOWS__)
#error This file is for Windows system
#endif
#include <sewer/nowarn.hxx>
#include <Windows.h>
#include <ShlObj.h>
#include <sewer/warn.hxx>
struct _dir_t
{
HANDLE handle;
WIN32_FIND_DATA find;
BOOL more_files;
DWORD find_next_error;
};
static const uint32_t i_NUM_RETRYS = 10;
static void i_file_error(ferror_t *error)
{
if (error != NULL)
{
DWORD err = GetLastError();
if (err == ERROR_ALREADY_EXISTS)
*error = ekFEXISTS;
else if (err == ERROR_PATH_NOT_FOUND)
*error = ekFNOPATH;
else if (err == ERROR_FILE_NOT_FOUND || err == ERROR_INVALID_NAME)
*error = ekFNOFILE;
else if (err == ERROR_DIR_NOT_EMPTY)
*error = ekFNOEMPTY;
else if (err == ERROR_ACCESS_DENIED)
*error = ekFNOACCESS;
else if (err == ERROR_SHARING_VIOLATION)
*error = ekFLOCK;
else
{
cassert_msg(FALSE, "Unknown file error");
*error = ekFUNDEF;
}
}
}
uint32_t bfile_dir_work(char_t *pathname, const uint32_t size)
{
WCHAR pathnamew[MAX_PATH + 1];
GetCurrentDirectory(sizeof(pathnamew), pathnamew);
return unicode_convers(cast_const(pathnamew, char_t), pathname, ekUTF16, ekUTF8, size);
}
bool_t bfile_dir_set_work(const char_t *pathname, ferror_t *error)
{
WCHAR pathnamew[MAX_PATH + 1];
unicode_convers(pathname, cast(pathnamew, char_t), ekUTF8, ekUTF16, sizeof(pathnamew));
if (SetCurrentDirectory(pathnamew) != 0)
{
ptr_assign(error, ekFOK);
return TRUE;
}
else
{
ptr_assign(error, ekFUNDEF);
return FALSE;
}
}
uint32_t bfile_dir_home(char_t *pathname, const uint32_t size)
{
WCHAR wname[MAX_PATH + 1];
if (SHGetFolderPath(NULL, CSIDL_PROFILE, NULL, 0, wname) == S_OK)
return unicode_convers(cast_const(wname, char_t), pathname, ekUTF16, ekUTF8, size);
pathname[0] = '\0';
return 1;
}
uint32_t bfile_dir_data(char_t *pathname, const uint32_t size)
{
WCHAR wname[MAX_PATH + 1];
if (SHGetFolderPath(NULL, CSIDL_APPDATA, NULL, 0, wname) == S_OK)
return unicode_convers(cast_const(wname, char_t), pathname, ekUTF16, ekUTF8, size);
pathname[0] = '\0';
return 1;
}
uint32_t bfile_dir_exec(char_t *pathname, const uint32_t size)
{
TCHAR wname[MAX_PATH + 1];
GetModuleFileName(NULL, wname, MAX_PATH + 1);
return unicode_convers(cast_const(wname, char_t), pathname, ekUTF16, ekUTF8, size);
}
uint32_t bfile_dir_tmp(char_t *pathname, const uint32_t size)
{
TCHAR wname[MAX_PATH + 1];
GetTempPathW(MAX_PATH + 1, wname);
return unicode_convers(cast_const(wname, char_t), pathname, ekUTF16, ekUTF8, size);
}
bool_t bfile_dir_create(const char_t *pathname, ferror_t *error)
{
WCHAR pathnamew[MAX_PATH + 1];
uint32_t num_bytes = unicode_convers(pathname, cast(pathnamew, char_t), ekUTF8, ekUTF16, sizeof(pathnamew));
if (num_bytes < sizeof(pathnamew))
{
if (CreateDirectory(pathnamew, NULL) != 0)
{
ptr_assign(error, ekFOK);
return TRUE;
}
else
{
i_file_error(error);
return FALSE;
}
}
else
{
ptr_assign(error, ekFBIGNAME);
return FALSE;
}
}
Dir *bfile_dir_open(const char_t *pathname, ferror_t *error)
{
WCHAR pathnamew[MAX_PATH + 10];
uint32_t num_bytes = unicode_convers(pathname, cast(pathnamew, char_t), ekUTF8, ekUTF16, sizeof(pathnamew));
if (num_bytes < sizeof(pathnamew))
{
HANDLE handle;
WIN32_FIND_DATA find;
wcscat_s(pathnamew, MAX_PATH + 10, L"\\*.*");
handle = FindFirstFile(pathnamew, &find);
if (handle != INVALID_HANDLE_VALUE)
{
Dir *dir = cast(bmem_malloc(sizeof(Dir)), Dir);
_osbs_directory_alloc();
dir->handle = handle;
dir->find = find;
dir->more_files = TRUE;
ptr_assign(error, ekFOK);
return dir;
}
else
{
i_file_error(error);
return NULL;
}
}
else
{
ptr_assign(error, ekFBIGNAME);
return NULL;
}
}
void bfile_dir_close(Dir **dir)
{
BOOL ok;
cassert_no_null(dir);
cassert_no_null(*dir);
ok = FindClose((*dir)->handle);
cassert_unref(ok == TRUE, ok);
bmem_free(*dcast(dir, byte_t));
*dir = NULL;
_osbs_directory_dealloc();
}
static file_type_t i_file_type(DWORD file_attrs)
{
if ((file_attrs & FILE_ATTRIBUTE_DIRECTORY) == FILE_ATTRIBUTE_DIRECTORY)
return ekDIRECTORY;
if ((file_attrs & FILE_ATTRIBUTE_DEVICE) == FILE_ATTRIBUTE_DEVICE)
return ekOTHERFILE;
if ((file_attrs & FILE_ATTRIBUTE_ARCHIVE) == FILE_ATTRIBUTE_ARCHIVE)
return ekARCHIVE;
if ((file_attrs & FILE_ATTRIBUTE_NORMAL) == FILE_ATTRIBUTE_NORMAL)
{
cassert(file_attrs == FILE_ATTRIBUTE_NORMAL);
return ekARCHIVE;
}
return ekOTHERFILE;
}
static void i_file_date(FILETIME *ftime, Date *date)
{
BOOL ok;
FILETIME ft;
SYSTEMTIME st;
cassert_no_null(ftime);
cassert_no_null(date);
ok = FileTimeToLocalFileTime(ftime, &ft);
FileTimeToSystemTime(&ft, &st);
cassert_unref(ok == TRUE, ok);
date->wday = (week_day_t)st.wDayOfWeek;
date->mday = (uint8_t)st.wDay;
date->month = (month_t)st.wMonth;
date->year = (uint16_t)st.wYear;
date->hour = (uint8_t)st.wHour;
date->minute = (uint8_t)st.wMinute;
date->second = (uint8_t)st.wSecond;
}
bool_t bfile_dir_get(Dir *dir, char_t *name, const uint32_t size, file_type_t *type, uint64_t *fsize, Date *updated, ferror_t *error)
{
cassert_no_null(dir);
if (dir->more_files == TRUE)
{
if (name != NULL)
{
uint32_t nb = unicode_convers(cast_const(dir->find.cFileName, char_t), name, ekUTF16, ekUTF8, size);
if (nb == size)
{
ptr_assign(error, ekFBIGNAME);
return FALSE;
}
}
if (type != NULL)
*type = i_file_type(dir->find.dwFileAttributes);
if (fsize != NULL)
{
LARGE_INTEGER lfsize;
lfsize.LowPart = dir->find.nFileSizeLow;
lfsize.HighPart = dir->find.nFileSizeHigh;
*fsize = (uint64_t)lfsize.QuadPart;
}
if (updated != NULL)
i_file_date(&dir->find.ftLastWriteTime, updated);
if (FindNextFile(dir->handle, &dir->find) == FALSE)
{
dir->find_next_error = GetLastError();
dir->more_files = FALSE;
}
ptr_assign(error, ekFOK);
return TRUE;
}
else
{
if (error != NULL)
{
if (dir->find_next_error == ERROR_NO_MORE_FILES)
*error = ekFNOFILES;
else
i_file_error(error);
}
return FALSE;
}
}
bool_t bfile_dir_delete(const char_t *pathname, ferror_t *error)
{
WCHAR pathnamew[MAX_PATH + 1];
uint32_t num_bytes = unicode_convers(pathname, cast(pathnamew, char_t), ekUTF8, ekUTF16, sizeof(pathnamew));
if (num_bytes < sizeof(pathnamew))
{
BOOL result = RemoveDirectory(pathnamew);
if (result == TRUE)
{
ptr_assign(error, ekFOK);
return TRUE;
}
else
{
i_file_error(error);
return FALSE;
}
}
else
{
ptr_assign(error, ekFBIGNAME);
return FALSE;
}
}
File *bfile_create(const char_t *pathname, ferror_t *error)
{
WCHAR pathnamew[MAX_PATH + 1];
uint32_t num_bytes = unicode_convers(pathname, cast(pathnamew, char_t), ekUTF8, ekUTF16, sizeof(pathnamew));
if (num_bytes < sizeof(pathnamew))
{
HANDLE file = CreateFile(pathnamew, GENERIC_READ | GENERIC_WRITE, FILE_SHARE_WRITE, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL);
if (file != INVALID_HANDLE_VALUE)
{
_osbs_file_alloc();
ptr_assign(error, ekFOK);
return (File *)file;
}
else
{
i_file_error(error);
return NULL;
}
}
else
{
ptr_assign(error, ekFBIGNAME);
return NULL;
}
}
File *bfile_open(const char_t *pathname, const file_mode_t mode, ferror_t *error)
{
WCHAR pathnamew[MAX_PATH + 1];
uint32_t num_bytes = unicode_convers(pathname, cast(pathnamew, char_t), ekUTF8, ekUTF16, sizeof(pathnamew));
if (num_bytes < sizeof(pathnamew))
{
DWORD access = 0;
HANDLE file = 0;
switch (mode)
{
case ekREAD:
access = GENERIC_READ;
break;
case ekWRITE:
case ekAPPEND:
access = GENERIC_READ | GENERIC_WRITE;
break;
cassert_default();
}
file = CreateFile(pathnamew, access, 0 , NULL , OPEN_EXISTING, 0, NULL );
if (file != INVALID_HANDLE_VALUE)
{
(void)_osbs_file_alloc();
if (mode == ekAPPEND)
SetFilePointer(file, 0, NULL, FILE_END);
ptr_assign(error, ekFOK);
#if defined(__x64__)
return (File *)(uint64_t)file;
#else
return cast(file, File);
#endif
}
else
{
i_file_error(error);
return NULL;
}
}
else
{
ptr_assign(error, ekFBIGNAME);
return NULL;
}
}
void bfile_close(File **file)
{
BOOL ok;
cassert_no_null(file);
cassert_no_null(*file);
ok = CloseHandle((HANDLE)*file);
cassert_unref(ok != 0, ok);
_osbs_file_dealloc();
*file = NULL;
}
bool_t bfile_lstat(const char_t *pathname, file_type_t *type, uint64_t *size, Date *updated, ferror_t *error)
{
WCHAR pathnamew[MAX_PATH + 1];
uint32_t num_bytes = unicode_convers(pathname, cast(pathnamew, char_t), ekUTF8, ekUTF16, sizeof(pathnamew));
if (num_bytes < sizeof(pathnamew))
{
WIN32_FILE_ATTRIBUTE_DATA attribs;
BOOL ok = 0;
uint32_t i = 0;
while ((ok = GetFileAttributesEx(pathnamew, GetFileExInfoStandard, &attribs)) == 0)
{
i++;
if (i == i_NUM_RETRYS)
break;
}
if (ok != 0)
{
if (type != NULL)
*type = i_file_type(attribs.dwFileAttributes);
if (size != NULL)
{
LARGE_INTEGER lsize;
lsize.LowPart = attribs.nFileSizeLow;
lsize.HighPart = attribs.nFileSizeHigh;
*size = (uint64_t)lsize.QuadPart;
}
if (updated != NULL)
i_file_date(&attribs.ftLastWriteTime, updated);
ptr_assign(error, ekFOK);
return TRUE;
}
else
{
ptr_assign(type, ekOTHERFILE);
ptr_assign(size, 0);
i_file_error(error);
return FALSE;
}
}
else
{
ptr_assign(error, ekFBIGNAME);
return FALSE;
}
}
bool_t bfile_fstat(const File *file, file_type_t *type, uint64_t *size, Date *updated, ferror_t *error)
{
cassert_no_null(file);
if (type != NULL)
{
DWORD ltype = GetFileType((HANDLE)file);
switch (ltype)
{
case FILE_TYPE_DISK:
*type = ekARCHIVE;
break;
case FILE_TYPE_CHAR:
*type = ekOTHERFILE;
break;
case FILE_TYPE_PIPE:
*type = ekOTHERFILE;
break;
case FILE_TYPE_UNKNOWN:
*type = ekOTHERFILE;
if (GetLastError() != 0)
{
break;
}
else
{
i_file_error(error);
return FALSE;
}
default:
cassert_msg(FALSE, "file_fstat: unknown type");
*type = ekOTHERFILE;
}
}
if (size != NULL)
{
LARGE_INTEGER lsize;
BOOL ok = GetFileSizeEx((HANDLE)file, &lsize);
if (ok != 0)
{
*size = (uint64_t)lsize.QuadPart;
}
else
{
i_file_error(error);
return FALSE;
}
}
if (updated != NULL)
{
BOOL ok;
FILETIME ft;
ok = GetFileTime((HANDLE)file, NULL, NULL, &ft);
cassert_unref(ok == TRUE, ok);
i_file_date(&ft, updated);
}
ptr_assign(error, ekFOK);
return TRUE;
}
bool_t bfile_read(File *file, byte_t *data, const uint32_t size, uint32_t *rsize, ferror_t *error)
{
DWORD lrsize = 0;
BOOL ok;
cassert_no_null(file);
cassert_no_null(data);
ok = ReadFile((HANDLE)file, (LPVOID)data, (DWORD)size, &lrsize, NULL);
if (ok != 0)
{
if (lrsize > 0)
{
ptr_assign(rsize, (uint32_t)lrsize);
ptr_assign(error, ekFOK);
return TRUE;
}
else
{
cassert(lrsize == 0);
ptr_assign(rsize, 0);
ptr_assign(error, ekFOK);
return FALSE;
}
}
else
{
i_file_error(error);
return FALSE;
}
}
bool_t bfile_write(File *file, const byte_t *data, const uint32_t size, uint32_t *wsize, ferror_t *error)
{
DWORD lwsize = 0;
BOOL ok;
cassert_no_null(file);
cassert_no_null(data);
ok = WriteFile((HANDLE)file, (LPVOID)data, (DWORD)size, &lwsize, NULL);
if (ok != 0)
{
ptr_assign(wsize, (uint32_t)lwsize);
ptr_assign(error, ekFOK);
return TRUE;
}
else
{
i_file_error(error);
return FALSE;
}
}
bool_t bfile_seek(File *file, const int64_t offset, const file_seek_t whence, ferror_t *error)
{
LARGE_INTEGER li;
DWORD method = 0;
DWORD ret;
cassert_no_null(file);
li.QuadPart = (LONGLONG)offset;
switch (whence)
{
case ekSEEKSET:
method = FILE_BEGIN;
break;
case ekSEEKCUR:
method = FILE_CURRENT;
break;
case ekSEEKEND:
method = FILE_END;
break;
cassert_default();
}
ret = SetFilePointer((HANDLE)file, li.LowPart, &li.HighPart, method);
if (ret != INVALID_SET_FILE_POINTER)
{
ptr_assign(error, ekFOK);
return TRUE;
}
else
{
if (GetLastError() == ERROR_NEGATIVE_SEEK)
{
ptr_assign(error, ekFSEEKNEG);
}
else
{
ptr_assign(error, ekFUNDEF);
}
return FALSE;
}
}
uint64_t bfile_pos(const File *file)
{
DWORD low = 0;
LONG high = 0;
cassert_no_null(file);
low = SetFilePointer((HANDLE)file, 0, &high, FILE_CURRENT);
return ((uint64_t)high << 32) | low;
}
bool_t bfile_delete(const char_t *pathname, ferror_t *error)
{
WCHAR pathnamew[MAX_PATH + 1];
uint32_t num_bytes = unicode_convers(pathname, cast(pathnamew, char_t), ekUTF8, ekUTF16, sizeof(pathnamew));
if (num_bytes < sizeof(pathnamew))
{
if (DeleteFile(pathnamew) != 0)
{
ptr_assign(error, ekFOK);
return TRUE;
}
else
{
i_file_error(error);
return FALSE;
}
}
else
{
ptr_assign(error, ekFBIGNAME);
return FALSE;
}
}
bool_t bfile_rename(const char_t *current_pathname, const char_t *new_pathname, ferror_t *error)
{
WCHAR current_pathnamew[MAX_PATH + 1];
WCHAR new_pathnamew[MAX_PATH + 1];
uint32_t num_bytes1 = unicode_convers(current_pathname, cast(current_pathnamew, char_t), ekUTF8, ekUTF16, sizeof(current_pathnamew));
uint32_t num_bytes2 = unicode_convers(new_pathname, cast(new_pathnamew, char_t), ekUTF8, ekUTF16, sizeof(new_pathnamew));
if (num_bytes1 < sizeof(current_pathnamew) && num_bytes2 < sizeof(new_pathnamew))
{
if (MoveFile(current_pathnamew, new_pathnamew) != 0)
{
ptr_assign(error, ekFOK);
return TRUE;
}
else
{
i_file_error(error);
return FALSE;
}
}
else
{
ptr_assign(error, ekFBIGNAME);
return FALSE;
}
}