#include <libidav/utils.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <errno.h>
#include <cx/string.h>
#ifndef _WIN32
#include <unistd.h>
#endif
#include "system.h"
void sys_freedirent(SysDirEnt *ent) {
free(ent->name);
free(ent);
}
#ifndef _WIN32
void sys_init(
void) {
}
void sys_uninit(
void) {
}
SYS_DIR sys_opendir(
const char *path) {
DIR *dir = opendir(path);
if(!dir) {
return NULL;
}
SysDir *d = malloc(
sizeof(SysDir));
d->dir = dir;
d->ent =
NULL;
return d;
}
SysDirEnt* sys_readdir(
SYS_DIR dir) {
if(dir->ent) {
free(dir->ent->name);
free(dir->ent);
dir->ent =
NULL;
}
struct dirent *ent = readdir(dir->dir);
if(ent) {
SysDirEnt *e = malloc(
sizeof(SysDirEnt));
e->name = strdup(ent->d_name);
dir->ent = e;
return e;
}
return NULL;
}
void sys_closedir(
SYS_DIR dir) {
closedir(dir->dir);
if(dir->ent) {
free(dir->ent->name);
free(dir->ent);
}
free(dir);
}
FILE* sys_fopen(
const char *path,
const char *mode) {
return fopen(path, mode);
}
int sys_stat(
const char *path,
SYS_STAT *s) {
return stat(path, s);
}
int sys_lstat(
const char *path,
SYS_STAT *s) {
return lstat(path, s);
}
int sys_islink(
const char *path) {
struct stat s;
if(!lstat(path, &s)) {
return S_ISLNK(s.st_mode);
}
return 0;
}
int sys_rename(
const char *oldpath,
const char *newpath) {
return rename(oldpath, newpath);
}
int sys_unlink(
const char *path) {
return unlink(path);
}
int sys_mkdir(
const char *path) {
return mkdir(path,
S_IRWXU |
S_IRGRP |
S_IXGRP |
S_IROTH |
S_IXOTH);
}
char* sys_readlink(
const char *path,
SYS_STAT *s) {
char *ret =
NULL;
off_t l_sz = s->st_size +
16;
size_t lnksize = l_sz >
256 ? l_sz :
256;
char *lnkbuf = malloc(lnksize);
ssize_t len =
0;
for(
int i=
0;i<
4;i++) {
len = readlink(path, lnkbuf, lnksize);
if(len < lnksize) {
ret = lnkbuf;
lnkbuf[len] =
0;
break;
}
lnksize *=
2;
lnkbuf = realloc(lnkbuf, lnksize);
}
if(!ret) {
free(lnkbuf);
}
return ret;
}
int sys_symlink(
const char *target,
const char *linkpath) {
int err = symlink(target, linkpath);
if(err && errno ==
EEXIST) {
if(unlink(linkpath)) {
return 1;
}
return sys_symlink(target, linkpath);
}
return err;
}
int sys_truncate(
const char* path,
off_t length) {
return truncate(path, length);
}
#else
#include <windows.h>
#include <winnls.h>
#include <shobjidl.h>
#include <objbase.h>
#include <objidl.h>
#include <direct.h>
#include <wchar.h>
void sys_init(
void) {
HRESULT res = CoInitialize(
NULL);
if(res !=
S_OK) {
fprintf(stderr,
"Error: CoInitialize failed\n");
}
}
void sys_uninit(
void) {
CoUninitialize();
}
static wchar_t* path2winpath(
const char *path,
int dir,
int *newlen) {
size_t len = strlen(path);
size_t lenadd = dir ?
2 :
0;
wchar_t *wpath = calloc(len+lenadd+
1,
sizeof(
wchar_t));
int wlen = MultiByteToWideChar(
CP_UTF8,
0,
path,
len,
wpath,
len+
1
);
if(newlen) {
*newlen = wlen;
}
for(
int i=
0;i<wlen;i++) {
if(wpath[i] ==
'/'L) {
wpath[i] =
'\\'L;
}
}
if(dir) {
if(wpath[wlen-
1] !=
'\\'L) {
wpath[wlen++] =
'\\'L;
}
wpath[wlen++] =
'*'L;
}
wpath[wlen] =
0;
return wpath;
}
static char* winpath2multibyte(
const wchar_t *wpath,
size_t wlen) {
size_t maxlen = wlen *
4;
char *ret = malloc(maxlen +
1);
int ret_len = WideCharToMultiByte(
CP_UTF8,
0,
wpath,
wlen,
ret,
maxlen,
NULL,
NULL);
ret[ret_len] =
0;
return ret;
}
SYS_DIR sys_opendir(
const char *path) {
struct WinDir *dir = malloc(
sizeof(
struct WinDir));
wchar_t *dirpath = path2winpath(path,
TRUE,
NULL);
if(!dirpath) {
fprintf(stderr,
"Cannot convert path \"%s\" to UTF16\n", path);
free(dir);
return NULL;
}
dir->first =
1;
dir->handle = FindFirstFileW(dirpath, &dir->finddata);
free(dirpath);
if(dir->handle ==
INVALID_HANDLE_VALUE) {
free(dir);
return NULL;
}
dir->ent =
NULL;
return dir;
}
SysDirEnt* sys_readdir(
SYS_DIR dir) {
if(dir->ent) {
free(dir->ent->name);
free(dir->ent);
dir->ent =
NULL;
}
if(dir->first) {
dir->first =
0;
}
else {
if(FindNextFileW(dir->handle, &dir->finddata) ==
0) {
return NULL;
}
}
size_t namelen = wcslen(dir->finddata.cFileName);
char *name = malloc((namelen+
1)*
4);
int nlen = WideCharToMultiByte(
CP_UTF8,
0,
dir->finddata.cFileName,
-
1,
name,
256,
NULL,
NULL);
if(nlen >
0) {
name[nlen] =
0;
SysDirEnt *ent = malloc(
sizeof(SysDirEnt));
ent->name = name;
dir->ent = ent;
return ent;
}
else {
return NULL;
}
}
void sys_closedir(
SYS_DIR dir) {
if(dir->ent) {
free(dir->ent->name);
free(dir->ent);
}
FindClose(dir->handle);
free(dir);
}
FILE* sys_fopen(
const char *path,
const char *mode) {
wchar_t *fpath = path2winpath(path,
FALSE,
NULL);
wchar_t *fmode = path2winpath(mode,
FALSE,
NULL);
FILE *file = (fpath && fmode) ? _wfopen(fpath, fmode) :
NULL;
free(fpath);
free(fmode);
return file;
}
int sys_stat(
const char *path,
SYS_STAT *s) {
wchar_t *fpath = path2winpath(path,
FALSE,
NULL);
if(!fpath) {
fprintf(stderr,
"Cannot convert path \"%s\" to UTF16\n", path);
return -
1;
}
int ret = _wstat64(fpath, s);
free(fpath);
return ret;
}
int sys_lstat(
const char *path,
SYS_STAT *s) {
return sys_stat(path, s);
}
int sys_islink(
const char *path) {
int ret =
0;
cxstring path_s = cx_str(path);
if(cx_strsuffix(path_s,
CX_STR(
".lnk"))) {
IShellLink *sl;
HRESULT hres;
hres = CoCreateInstance(&CLSID_ShellLink,
NULL,
CLSCTX_INPROC_SERVER, &IID_IShellLinkW, (
LPVOID*)&sl);
if(
SUCCEEDED(hres)) {
IPersistFile *file;
hres = sl->lpVtbl->QueryInterface(sl, &IID_IPersistFile, (
void**)&file);
if(!
SUCCEEDED(hres)) {
sl->lpVtbl->Release(sl);
return ret;
}
int newlen =
0;
wchar_t *wpath = path2winpath(path,
0, &newlen);
hres = file->lpVtbl->Load(file, wpath,
STGM_READ);
if(
SUCCEEDED(hres)) {
ret =
1;
file->lpVtbl->Release(file);
}
free(wpath);
sl->lpVtbl->Release(sl);
}
}
return ret;
}
int sys_rename(
const char *oldpath,
const char *newpath) {
wchar_t *o = path2winpath(oldpath,
FALSE,
NULL);
wchar_t *n = path2winpath(newpath,
FALSE,
NULL);
if(!o || !n) {
return -
1;
}
struct __stat64 s;
if(!_wstat64(n, &s)) {
if(_wunlink(n)) {
fprintf(stderr,
"sys_rename: cannot delete existing file: %ls\n", n);
}
}
int ret = _wrename(o, n);
free(o);
free(n);
return ret;
}
int sys_unlink(
const char *path) {
wchar_t *wpath = path2winpath(path,
FALSE,
NULL);
if(!wpath) {
fprintf(stderr,
"sys_unlink: cannot convert path\n");
return -
1;
}
int ret = _wunlink(wpath);
free(wpath);
return ret;
}
int sys_mkdir(
const char *path) {
wchar_t *wpath = path2winpath(path,
FALSE,
NULL);
if(!wpath) {
fprintf(stderr,
"sys_mkdir: cannot convert path\n");
return -
1;
}
int ret = _wmkdir(wpath);
free(wpath);
return ret;
}
char* sys_readlink(
const char *path,
SYS_STAT *s) {
char *ret_link =
NULL;
IShellLinkW *sl;
HRESULT hres = CoCreateInstance(&CLSID_ShellLink,
NULL,
CLSCTX_INPROC_SERVER, &IID_IShellLinkW, (
LPVOID*)&sl);
if(!
SUCCEEDED(hres)) {
return NULL;
}
IPersistFile *file;
hres = sl->lpVtbl->QueryInterface(sl, &IID_IPersistFile, (
void**)&file);
if(!
SUCCEEDED(hres)) {
sl->lpVtbl->Release(sl);
return NULL;
}
int newlen =
0;
wchar_t *wpath = path2winpath(path,
0, &newlen);
hres = file->lpVtbl->Load(file, wpath,
STGM_READ);
if(
SUCCEEDED(hres)) {
WCHAR link_path[
MAX_PATH];
memset(link_path,
0,
MAX_PATH);
hres = sl->lpVtbl->Resolve(sl,
0,
SLR_NO_UI);
if(
SUCCEEDED(hres)) {
hres = sl->lpVtbl->GetPath(sl, link_path,
MAX_PATH,
NULL,
SLGP_SHORTPATH);
if(
SUCCEEDED(hres)) {
ret_link = winpath2multibyte(link_path, wcslen(link_path));
}
}
}
free(wpath);
file->lpVtbl->Release(file);
sl->lpVtbl->Release(sl);
return ret_link;
}
int sys_symlink(
const char *target,
const char *linkpath) {
char *link_parent = util_parent_path(linkpath);
char *target_unnormalized = util_concat_path(link_parent, target);
char *target_normalized = util_path_normalize(target_unnormalized);
free(link_parent);
free(target_unnormalized);
int wtargetlen =
0;
wchar_t *wtarget = path2winpath(target_normalized,
FALSE, &wtargetlen);
free(target_normalized);
if(!wtarget) {
return 1;
}
int wlinkpathlen =
0;
wchar_t *wlinkpath = path2winpath(linkpath,
FALSE, &wlinkpathlen);
if(!wlinkpath) {
free(wtarget);
return 1;
}
int ret =
1;
IShellLinkW *sl;
HRESULT hres = CoCreateInstance(&CLSID_ShellLink,
NULL,
CLSCTX_INPROC_SERVER, &IID_IShellLinkW, (
LPVOID*)&sl);
if(
SUCCEEDED(hres)) {
IPersistFile *file;
hres = sl->lpVtbl->QueryInterface(sl, &IID_IPersistFile, (
void**)&file);
if(
SUCCEEDED(hres)) {
file->lpVtbl->Load(file, wlinkpath,
STGM_READ);
hres = sl->lpVtbl->SetPath(sl, wtarget);
if(
SUCCEEDED(hres)) {
hres = file->lpVtbl->Save(file, wlinkpath,
TRUE);
if(
SUCCEEDED(hres)) {
ret =
0;
}
}
file->lpVtbl->Release(file);
}
sl->lpVtbl->Release(sl);
}
free(wtarget);
free(wlinkpath);
return ret;
}
int sys_truncate(
const char* path,
off_t length) {
wchar_t* wpath = path2winpath(path,
FALSE,
NULL);
if (!wpath) {
fprintf(stderr,
"sys_truncate: cannot convert path\n");
return -
1;
}
FILE* file = _wfopen(wpath,
"wb"L);
int ret =
1;
if (file) {
ret = _chsize(fileno(file), length);
fclose(file);
}
free(wpath);
return ret;
}
#endif