dav/system.c

changeset 611
a7c48e0dca88
parent 608
3e4c0285a868
child 612
66dc8b992d8d
--- a/dav/system.c	Fri Aug 02 21:40:05 2019 +0200
+++ b/dav/system.c	Fri Aug 02 22:04:00 2019 +0200
@@ -26,6 +26,8 @@
  * POSSIBILITY OF SUCH DAMAGE.
  */
 
+#include <libidav/utils.h>
+
 #include <stdio.h>
 #include <stdlib.h>
 #include <string.h>
@@ -34,6 +36,8 @@
 #include <sys/types.h>
 #include <errno.h>
 
+#include <ucx/string.h>
+
 #ifndef _WIN32
 #include <unistd.h>
 #endif
@@ -48,6 +52,13 @@
 #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) {
@@ -96,6 +107,13 @@
     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);
 }
@@ -108,8 +126,31 @@
     return mkdir(path, S_IRWXU | S_IRGRP | S_IXGRP | S_IROTH | S_IXOTH);
 }
 
-ssize_t sys_readlink(const char *path, char *buffer, size_t size) {
-    return readlink(path, buffer, size);
+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) {
@@ -126,6 +167,26 @@
 #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;
@@ -158,7 +219,25 @@
     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));
@@ -247,7 +326,43 @@
 }
 
 int sys_lstat(const char *path, SYS_STAT *s) {
-	return sys_stat(path, s); // unsupported on windows
+    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;
+    
+    scstr_t path_s = scstr(path);
+    if(scstrsuffix(path_s, SC(".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) {
@@ -292,16 +407,103 @@
     return ret;
 }
 
-ssize_t sys_readlink(const char *path, char *buffer, size_t size) {
-    // TODO
-    fprintf(stderr, "sys_readlink: implement me\n");
-    return 1;
+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) {
-    // TODO
-    fprintf(stderr, "sys_symlink: implement me\n");
-    return 1;
+    // 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;
 }
 
 #endif

mercurial