Sun, 10 Nov 2024 15:30:46 +0100
update toolkit
/* * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. * * Copyright 2024 Olaf Wintermann. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. */ #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 /* ---------- POSIX implementation ---------- */ 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++) { // we try to read the link at most 4 times // only repeat if the buffer is too small len = readlink(path, lnkbuf, lnksize); if(len < lnksize) { ret = lnkbuf; // success lnkbuf[len] = 0; // terminate buffer break; } lnksize *= 2; // retry with bigger buffer 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 /* ---------- Windows implementation ---------- */ #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); // unsupported on windows } int sys_islink(const char *path) { // don't use symlinks on windows, because it is not really useful // however, we interpret .lnk files as symlinks int ret = 0; cxstring path_s = cx_str(path); if(cx_strsuffix(path_s, CX_STR(".lnk"))) { // looks like a .lnk file // check content 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; // create COM object for using the ShellLink interface 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; } // load .lnk file 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)); } } } // cleanup free(wpath); file->lpVtbl->Release(file); sl->lpVtbl->Release(sl); return ret_link; } int sys_symlink(const char *target, const char *linkpath) { // convert relative target to absolut path 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); // convert to wchar_t* 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; // create COM object for using the ShellLink interface 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)) { // try to load the shortcut file->lpVtbl->Load(file, wlinkpath, STGM_READ); // ignore error // set path hres = sl->lpVtbl->SetPath(sl, wtarget); if(SUCCEEDED(hres)) { hres = file->lpVtbl->Save(file, wlinkpath, TRUE); if(SUCCEEDED(hres)) { // successfully created/modified shortcut ret = 0; // ok } } 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, L"wb"); int ret = 1; if (file) { ret = _chsize(fileno(file), length); fclose(file); } free(wpath); return ret; } #endif