move resource_get_remote_change to a separate source file dav-2

Tue, 09 Sep 2025 16:01:30 +0200

author
Olaf Wintermann <olaf.wintermann@gmail.com>
date
Tue, 09 Sep 2025 16:01:30 +0200
branch
dav-2
changeset 885
591377a27fa3
parent 884
11ddb45182d9
child 886
da79af4baec8

move resource_get_remote_change to a separate source file

dav/obj.mk file | annotate | diff | comparison | revisions
dav/pull.c file | annotate | diff | comparison | revisions
dav/pull.h file | annotate | diff | comparison | revisions
dav/sync.c file | annotate | diff | comparison | revisions
dav/syncdir.c file | annotate | diff | comparison | revisions
dav/syncdir.h file | annotate | diff | comparison | revisions
--- a/dav/obj.mk	Mon Sep 08 12:33:48 2025 +0200
+++ b/dav/obj.mk	Tue Sep 09 16:01:30 2025 +0200
@@ -42,6 +42,7 @@
 DAV_SRC = tar.c
 
 SYNC_SRC += sync.c
+SYNC_SRC += syncdir.c
 SYNC_SRC += scfg.c
 SYNC_SRC += db.c
 SYNC_SRC += tags.c
--- a/dav/pull.c	Mon Sep 08 12:33:48 2025 +0200
+++ b/dav/pull.c	Tue Sep 09 16:01:30 2025 +0200
@@ -27,4 +27,261 @@
  */
 
 #include "pull.h"
+#include "syncdir.h"
 
+#include <libidav/utils.h>
+#include <errno.h>
+#include <string.h>
+
+/*
+ * strcmp version that works with NULL pointers
+ */
+static int nullstrcmp(const char *s1, const char *s2) {
+    if(!s1 && s2) {
+        return -1;
+    }
+    if(s1 && !s2) {
+        return 1;
+    }
+    if(!s1 && !s2) {
+        return 0;
+    }
+    return strcmp(s1, s2);
+}
+
+static char* nullstrdup(const char *s) {
+    return s ? strdup(s) : NULL;
+}
+
+
+
+RemoteChangeType resource_get_remote_change(
+        CmdArgs *a,
+        DavResource *res,
+        SyncDirectory *dir,
+        SyncDatabase *db)
+{
+    DavBool update_db = FALSE;
+    
+    char *etag = dav_get_string_property(res, "D:getetag");
+    if(!etag) {
+        fprintf(stderr, "Error: resource %s has no etag\n", res->path);
+        return REMOTE_NO_CHANGE;
+    }
+    char *hash = sync_get_content_hash(res);
+      
+    DavBool issplit = dav_get_property(res, "idav:split") ? TRUE : FALSE;
+    if(issplit) {
+        util_remove_trailing_pathseparator(res->path);
+    }
+    DavBool iscollection = res->iscollection && !issplit;
+    
+    RemoteChangeType type = cmd_getoption(a, "conflict") ?
+            REMOTE_CHANGE_MODIFIED : REMOTE_CHANGE_CONFLICT_LOCAL_MODIFIED;
+    
+    LocalResource *local = cxMapGet(db->resources, dav_resource_path_key(res));
+    char *local_path = syncdir_create_local_path(dir, res->path);
+    
+    char *link = SYNC_SYMLINK(dir) ? 
+            dav_get_string_property_ns(res, DAV_PROPS_NS, "link") : NULL;
+    
+    SYS_STAT s;
+    DavBool exists = 1;
+    if(sys_stat(local_path, &s)) {
+        if(errno != ENOENT) {
+            fprintf(stderr, "Cannot stat file: %s\n", local_path);
+            free(local_path);
+            return REMOTE_NO_CHANGE;
+        }
+        exists = 0;
+    }
+    
+    RemoteChangeType ret = REMOTE_NO_CHANGE;
+    if(iscollection) {
+        if(!exists) {
+            ret = REMOTE_CHANGE_MKDIR;
+        } else if(local && S_ISDIR(s.st_mode)) {
+            local->isdirectory = 1; // make sure isdirectory is set
+        } else {
+            // set change to REMOTE_CHANGE_MKDIR, which will fail later
+            ret = REMOTE_CHANGE_MKDIR;
+        }
+    } else if(local) {
+        DavBool nochange = FALSE;
+        if(SYNC_SYMLINK(dir) && nullstrcmp(link, local->link_target)) {
+            ret = REMOTE_CHANGE_LINK;
+            nochange = TRUE;
+            
+            if(local->link_target) {
+                LocalResource *local2 = local_resource_new(dir, db, local->path);
+                if(type == REMOTE_CHANGE_CONFLICT_LOCAL_MODIFIED && nullstrcmp(local->link_target, local2->link_target)) {
+                    ret = REMOTE_CHANGE_CONFLICT_LOCAL_MODIFIED;
+                }            
+                local_resource_free(local2);
+
+                if(!nullstrcmp(link, local->link_target)) {
+                    ret = REMOTE_NO_CHANGE;
+                    update_db = TRUE;
+                }
+            } 
+        } else if(issplit && local->hash && hash) {
+            if(!strcmp(local->hash, hash)) {
+                // resource is already up-to-date on the client
+                nochange = TRUE;
+            }
+        } else if(local->etag) {
+            cxstring e = cx_str(etag);
+            if(cx_strprefix(e, CX_STR("W/"))) {
+                e = cx_strsubs(e, 2);
+            }
+            if(!strcmp(e.ptr, local->etag)) {
+                // resource is already up-to-date on the client
+                nochange = TRUE;
+            }
+        }
+        
+        if(!nochange) {
+            if(!(exists && s.st_mtime != local->last_modified)) {
+                type = REMOTE_CHANGE_MODIFIED;
+            }
+            ret = type;
+        }
+    } else if(link) {
+        // new file is a link
+        ret = REMOTE_CHANGE_LINK;
+        
+        if(exists && type == REMOTE_CHANGE_CONFLICT_LOCAL_MODIFIED) {
+            // a file with the same name already exists
+            // if it is a link, compare the targets
+            LocalResource *local2 = local_resource_new(dir, db, res->path);
+            if(local2) {
+                if(local2->link_target) {
+                    if(strcmp(link, local2->link_target)) {
+                        ret = REMOTE_CHANGE_CONFLICT_LOCAL_MODIFIED;
+                    }
+                } else {
+                    ret = REMOTE_CHANGE_CONFLICT_LOCAL_MODIFIED;
+                }
+                
+                local_resource_free(local2);
+            }
+        }
+        
+    } else if(exists) {
+        ret = type;
+    } else {
+        ret = REMOTE_CHANGE_NEW;
+    }
+     
+    // if hashing is enabled we can compare the hash of the remote file
+    // with the local file to test if a file is really modified
+    char *update_hash = NULL;
+    if (!iscollection &&
+        !link &&
+        (ret == REMOTE_CHANGE_MODIFIED || ret == REMOTE_CHANGE_CONFLICT_LOCAL_MODIFIED) &&
+        exists &&
+        hash &&
+        !dir->pull_skip_hashing)
+    {
+        // because rehashing a file is slow, there is a config element for
+        // disabling this (pull-skip-hashing)
+        char *local_hash = util_file_hash(local_path);
+        if(local_hash) {
+            if(!strcmp(hash, local_hash)) {
+                ret = REMOTE_NO_CHANGE;
+                update_db = TRUE;
+                update_hash = local_hash;
+                
+                // if local already exists, update the hash here
+                // because it is possible that there are metadata updates
+                // and in this case the db will updated later and needs
+                // the current hash
+                if(local) {
+                    if(local->hash) {
+                        free(local->hash);
+                    }
+                    local->hash = local_hash;
+                }
+            } else {
+                free(local_hash);
+            }
+        }
+    }
+    
+    // if a file is not modified, check if the metadata has changed
+    while(ret == REMOTE_NO_CHANGE && local) {
+        // check if tags have changed
+        if(dir->tagconfig) {
+            DavXmlNode *tagsprop = dav_get_property_ns(res, DAV_PROPS_NS, "tags");
+            CxList *remote_tags = NULL;
+            if(tagsprop) {
+                remote_tags = parse_dav_xml_taglist(tagsprop);
+            }
+            char *remote_hash = create_tags_hash(remote_tags);
+            if(nullstrcmp(remote_hash, local->remote_tags_hash)) {
+                ret = REMOTE_CHANGE_METADATA;
+            }
+            if(remote_hash) {
+                free(remote_hash);
+            }
+            free_taglist(remote_tags);
+            
+            if(ret == REMOTE_CHANGE_METADATA) {
+                break;
+            }
+        }
+        
+        // check if extended attributes have changed
+        if((dir->metadata & FINFO_XATTR) == FINFO_XATTR) {
+            DavXmlNode *xattr = dav_get_property_ns(res, DAV_PROPS_NS, "xattributes");
+            char *xattr_hash = get_xattr_hash(xattr);
+            if(nullstrcmp(xattr_hash, local->xattr_hash)) {
+                ret = REMOTE_CHANGE_METADATA;
+                break;
+            }
+        } 
+        
+        // check if finfo has changed
+        DavXmlNode *finfo = dav_get_property_ns(res, DAV_PROPS_NS, "finfo");
+        if((dir->metadata & FINFO_MODE) == FINFO_MODE) {
+            FileInfo f;
+            finfo_get_values(finfo, &f);
+            if(f.mode_set && f.mode != local->mode) {
+                ret = REMOTE_CHANGE_METADATA;
+                break;
+            }
+        }
+        
+        break;
+    }
+    
+    // if update_db is set, a file was modified on the server, but we already
+    // have the file content, but we need to update the db
+    if(ret == REMOTE_NO_CHANGE && update_db) {
+        if(!local) {
+            local = calloc(1, sizeof(LocalResource));
+            local->path = strdup(res->path);
+            
+            cxMapPut(db->resources, cx_hash_key_str(local->path), local);
+        }
+        
+        // update local res
+        SYS_STAT statdata;
+        if(!sys_stat(local_path, &statdata)) {
+            sync_set_metadata_from_stat(local, &statdata);
+        } else {
+            fprintf(stderr, "stat failed for file: %s : %s", local_path, strerror(errno));
+        }
+        local_resource_set_etag(local, etag);
+        if(!local->hash) {
+            local->hash = update_hash;
+        } // else: hash already updated
+        if(link) {
+            free(local->link_target);
+            local->link_target = link;
+        }
+    }
+        
+    free(local_path);
+    return ret;
+}
--- a/dav/pull.h	Mon Sep 08 12:33:48 2025 +0200
+++ b/dav/pull.h	Tue Sep 09 16:01:30 2025 +0200
@@ -29,6 +29,8 @@
 #ifndef PULL_H
 #define PULL_H
 
+#include "sync.h"
+
 #ifdef __cplusplus
 extern "C" {
 #endif
--- a/dav/sync.c	Mon Sep 08 12:33:48 2025 +0200
+++ b/dav/sync.c	Tue Sep 09 16:01:30 2025 +0200
@@ -68,6 +68,7 @@
 #include "libxattr.h"
 #include "tags.h"
 #include "connect.h"
+#include "syncdir.h"
 
 #include "system.h"
 
@@ -201,12 +202,6 @@
     return s ? strdup(s) : NULL;
 }
 
-static void nullfree(void *p) {
-    if(p) {
-        free(p);
-    }
-}
-
 
 
 static CxMapIterator mapIteratorValues(CxMap *map) {
@@ -452,14 +447,6 @@
 
 #endif
 
-static char* create_local_path(SyncDirectory *dir, const char *path) {
-    char *local_path = util_concat_path(dir->path, path);
-    size_t local_path_len = strlen(local_path);
-    if(local_path[local_path_len-1] == '/') {
-        local_path[local_path_len-1] = '\0';
-    }
-    return local_path;
-}
 
 static int res_matches_filter(Filter *filter, char *res_path) {
     // include/exclude filter
@@ -1027,7 +1014,7 @@
         if(local) {
             log_printf("update: %s\n", res->path);
             char *res_path = resource_local_path(res);
-            char *local_path = create_local_path(dir, res->path);
+            char *local_path = syncdir_create_local_path(dir, res->path);
             free(res_path);
             if(sync_store_metadata(dir, local_path, local, res)) {
                 fprintf(stderr, "Metadata update failed: %s\n", res->path);
@@ -1125,237 +1112,6 @@
     return ret;
 }
 
-RemoteChangeType resource_get_remote_change(
-        CmdArgs *a,
-        DavResource *res,
-        SyncDirectory *dir,
-        SyncDatabase *db)
-{
-    DavBool update_db = FALSE;
-    
-    char *etag = dav_get_string_property(res, "D:getetag");
-    if(!etag) {
-        fprintf(stderr, "Error: resource %s has no etag\n", res->path);
-        return REMOTE_NO_CHANGE;
-    }
-    char *hash = sync_get_content_hash(res);
-      
-    DavBool issplit = dav_get_property(res, "idav:split") ? TRUE : FALSE;
-    if(issplit) {
-        util_remove_trailing_pathseparator(res->path);
-    }
-    DavBool iscollection = res->iscollection && !issplit;
-    
-    RemoteChangeType type = cmd_getoption(a, "conflict") ?
-            REMOTE_CHANGE_MODIFIED : REMOTE_CHANGE_CONFLICT_LOCAL_MODIFIED;
-    
-    LocalResource *local = cxMapGet(db->resources, dav_resource_path_key(res));
-    char *local_path = create_local_path(dir, res->path);
-    
-    char *link = SYNC_SYMLINK(dir) ? 
-            dav_get_string_property_ns(res, DAV_PROPS_NS, "link") : NULL;
-    
-    SYS_STAT s;
-    DavBool exists = 1;
-    if(sys_stat(local_path, &s)) {
-        if(errno != ENOENT) {
-            fprintf(stderr, "Cannot stat file: %s\n", local_path);
-            free(local_path);
-            return REMOTE_NO_CHANGE;
-        }
-        exists = 0;
-    }
-    
-    RemoteChangeType ret = REMOTE_NO_CHANGE;
-    if(iscollection) {
-        if(!exists) {
-            ret = REMOTE_CHANGE_MKDIR;
-        } else if(local && S_ISDIR(s.st_mode)) {
-            local->isdirectory = 1; // make sure isdirectory is set
-        } else {
-            // set change to REMOTE_CHANGE_MKDIR, which will fail later
-            ret = REMOTE_CHANGE_MKDIR;
-        }
-    } else if(local) {
-        DavBool nochange = FALSE;
-        if(SYNC_SYMLINK(dir) && nullstrcmp(link, local->link_target)) {
-            ret = REMOTE_CHANGE_LINK;
-            nochange = TRUE;
-            
-            if(local->link_target) {
-                LocalResource *local2 = local_resource_new(dir, db, local->path);
-                if(type == REMOTE_CHANGE_CONFLICT_LOCAL_MODIFIED && nullstrcmp(local->link_target, local2->link_target)) {
-                    ret = REMOTE_CHANGE_CONFLICT_LOCAL_MODIFIED;
-                }            
-                local_resource_free(local2);
-
-                if(!nullstrcmp(link, local->link_target)) {
-                    ret = REMOTE_NO_CHANGE;
-                    update_db = TRUE;
-                }
-            } 
-        } else if(issplit && local->hash && hash) {
-            if(!strcmp(local->hash, hash)) {
-                // resource is already up-to-date on the client
-                nochange = TRUE;
-            }
-        } else if(local->etag) {
-            cxstring e = cx_str(etag);
-            if(cx_strprefix(e, CX_STR("W/"))) {
-                e = cx_strsubs(e, 2);
-            }
-            if(!strcmp(e.ptr, local->etag)) {
-                // resource is already up-to-date on the client
-                nochange = TRUE;
-            }
-        }
-        
-        if(!nochange) {
-            if(!(exists && s.st_mtime != local->last_modified)) {
-                type = REMOTE_CHANGE_MODIFIED;
-            }
-            ret = type;
-        }
-    } else if(link) {
-        // new file is a link
-        ret = REMOTE_CHANGE_LINK;
-        
-        if(exists && type == REMOTE_CHANGE_CONFLICT_LOCAL_MODIFIED) {
-            // a file with the same name already exists
-            // if it is a link, compare the targets
-            LocalResource *local2 = local_resource_new(dir, db, res->path);
-            if(local2) {
-                if(local2->link_target) {
-                    if(strcmp(link, local2->link_target)) {
-                        ret = REMOTE_CHANGE_CONFLICT_LOCAL_MODIFIED;
-                    }
-                } else {
-                    ret = REMOTE_CHANGE_CONFLICT_LOCAL_MODIFIED;
-                }
-                
-                local_resource_free(local2);
-            }
-        }
-        
-    } else if(exists) {
-        ret = type;
-    } else {
-        ret = REMOTE_CHANGE_NEW;
-    }
-     
-    // if hashing is enabled we can compare the hash of the remote file
-    // with the local file to test if a file is really modified
-    char *update_hash = NULL;
-    if (!iscollection &&
-        !link &&
-        (ret == REMOTE_CHANGE_MODIFIED || ret == REMOTE_CHANGE_CONFLICT_LOCAL_MODIFIED) &&
-        exists &&
-        hash &&
-        !dir->pull_skip_hashing)
-    {
-        // because rehashing a file is slow, there is a config element for
-        // disabling this (pull-skip-hashing)
-        char *local_hash = util_file_hash(local_path);
-        if(local_hash) {
-            if(!strcmp(hash, local_hash)) {
-                ret = REMOTE_NO_CHANGE;
-                update_db = TRUE;
-                update_hash = local_hash;
-                
-                // if local already exists, update the hash here
-                // because it is possible that there are metadata updates
-                // and in this case the db will updated later and needs
-                // the current hash
-                if(local) {
-                    if(local->hash) {
-                        free(local->hash);
-                    }
-                    local->hash = local_hash;
-                }
-            } else {
-                free(local_hash);
-            }
-        }
-    }
-    
-    // if a file is not modified, check if the metadata has changed
-    while(ret == REMOTE_NO_CHANGE && local) {
-        // check if tags have changed
-        if(dir->tagconfig) {
-            DavXmlNode *tagsprop = dav_get_property_ns(res, DAV_PROPS_NS, "tags");
-            CxList *remote_tags = NULL;
-            if(tagsprop) {
-                remote_tags = parse_dav_xml_taglist(tagsprop);
-            }
-            char *remote_hash = create_tags_hash(remote_tags);
-            if(nullstrcmp(remote_hash, local->remote_tags_hash)) {
-                ret = REMOTE_CHANGE_METADATA;
-            }
-            if(remote_hash) {
-                free(remote_hash);
-            }
-            free_taglist(remote_tags);
-            
-            if(ret == REMOTE_CHANGE_METADATA) {
-                break;
-            }
-        }
-        
-        // check if extended attributes have changed
-        if((dir->metadata & FINFO_XATTR) == FINFO_XATTR) {
-            DavXmlNode *xattr = dav_get_property_ns(res, DAV_PROPS_NS, "xattributes");
-            char *xattr_hash = get_xattr_hash(xattr);
-            if(nullstrcmp(xattr_hash, local->xattr_hash)) {
-                ret = REMOTE_CHANGE_METADATA;
-                break;
-            }
-        } 
-        
-        // check if finfo has changed
-        DavXmlNode *finfo = dav_get_property_ns(res, DAV_PROPS_NS, "finfo");
-        if((dir->metadata & FINFO_MODE) == FINFO_MODE) {
-            FileInfo f;
-            finfo_get_values(finfo, &f);
-            if(f.mode_set && f.mode != local->mode) {
-                ret = REMOTE_CHANGE_METADATA;
-                break;
-            }
-        }
-        
-        break;
-    }
-    
-    // if update_db is set, a file was modified on the server, but we already
-    // have the file content, but we need to update the db
-    if(ret == REMOTE_NO_CHANGE && update_db) {
-        if(!local) {
-            local = calloc(1, sizeof(LocalResource));
-            local->path = strdup(res->path);
-            
-            cxMapPut(db->resources, cx_hash_key_str(local->path), local);
-        }
-        
-        // update local res
-        SYS_STAT statdata;
-        if(!sys_stat(local_path, &statdata)) {
-            sync_set_metadata_from_stat(local, &statdata);
-        } else {
-            fprintf(stderr, "stat failed for file: %s : %s", local_path, strerror(errno));
-        }
-        local_resource_set_etag(local, etag);
-        if(!local->hash) {
-            local->hash = update_hash;
-        } // else: hash already updated
-        if(link) {
-            nullfree(local->link_target);
-            local->link_target = link;
-        }
-    }
-        
-    free(local_path);
-    return ret;
-}
-
 void sync_set_metadata_from_stat(LocalResource *local, SYS_STAT *s) {
     local->last_modified = s->st_mtime;
     local->mode = s->st_mode & 07777;
@@ -1594,10 +1350,10 @@
     char *local_path;
     if(link) {
         char *res_path = resource_local_path(res);
-        local_path = create_local_path(dir, res_path);
+        local_path = syncdir_create_local_path(dir, res_path);
         free(res_path);
     } else {
-        local_path = create_local_path(dir, path);
+        local_path = syncdir_create_local_path(dir, path);
     }
     
     char *etag = dav_get_string_property(res, "D:getetag");
@@ -1782,7 +1538,7 @@
     
     // TODO: use resource_local_path return value (necessary for creating links on windows)
     //char *res_path = resource_local_path(res);
-    char *local_path = create_local_path(dir, res->path);
+    char *local_path = syncdir_create_local_path(dir, res->path);
     //free(res_path);
      
     log_printf("get: %s\n", res->path);
@@ -1841,7 +1597,7 @@
 }
 
 int sync_remove_local_resource(SyncDirectory *dir, LocalResource *res) {
-    char *local_path = create_local_path(dir, res->path);
+    char *local_path = syncdir_create_local_path(dir, res->path);
     SYS_STAT s;
     if(sys_stat(local_path, &s)) {
         free(local_path);
@@ -1873,7 +1629,7 @@
 
 int sync_remove_local_directory(SyncDirectory *dir, LocalResource *res) {
     int ret = 0;
-    char *local_path = create_local_path(dir, res->path);
+    char *local_path = syncdir_create_local_path(dir, res->path);
     
     log_printf("delete: %s\n", res->path);
     if(rmdir(local_path)) {
@@ -1892,7 +1648,7 @@
 }
 
 void rename_conflict_file(SyncDirectory *dir, SyncDatabase *db, char *path, DavBool copy) {
-    char *local_path = create_local_path(dir, path);
+    char *local_path = syncdir_create_local_path(dir, path);
     char *parent = util_parent_path(local_path);
     
     renamefunc fn = copy ? copy_file : sys_rename;
@@ -2379,9 +2135,9 @@
             if(dav_exists(res)) {
                 log_printf("conflict: %s\n", local->path);
                 local->last_modified = 0;
-                nullfree(local->etag);
+                free(local->etag);
                 local->etag = NULL;
-                nullfree(local->hash);
+                free(local->hash);
                 local->hash = NULL;
                 local->skipped = TRUE;
                 sync_conflict++;
@@ -2457,9 +2213,9 @@
                 } else if(cdt && changed) {
                     log_printf("conflict: %s\n", local_res->path);
                     local_res->last_modified = 0;
-                    nullfree(local_res->etag);
+                    free(local_res->etag);
                     local_res->etag = NULL;
-                    nullfree(local_res->hash);
+                    free(local_res->hash);
                     local_res->hash = NULL;
                     local_res->skipped = TRUE;
                     sync_conflict++;
@@ -2698,7 +2454,7 @@
             }
         }
         
-        char *file_path = create_local_path(dir, resource->path);
+        char *file_path = syncdir_create_local_path(dir, resource->path);
         SYS_STAT s;
         if(sys_stat(file_path, &s)) {
             if(errno == ENOENT) {
@@ -2811,7 +2567,7 @@
             // download the resource
             if(!sync_shutdown) {
                 if(resource->isdirectory) {
-                    char *local_path = create_local_path(dir, res->path);
+                    char *local_path = syncdir_create_local_path(dir, res->path);
                     if(sys_mkdir(local_path) && errno != EEXIST) {
                         log_error(
                                 "Cannot create directory %s: %s",
@@ -2826,7 +2582,7 @@
                         LocalResource *lr = cxMapGet(db->resources, cx_hash_key_str(res->path));
                         if(lr) {
                             lr->last_modified = 0;
-                            nullfree(lr->hash);
+                            free(lr->hash);
                             lr->hash = NULL;
                         } // else should not happen
                     }
@@ -2999,7 +2755,7 @@
         
         char *p = cxListAt(stack, 0);
         cxListRemove(stack, 0);
-        char *local_path = create_local_path(dir, p);
+        char *local_path = syncdir_create_local_path(dir, p);
         SYS_DIR local_dir = sys_opendir(local_path);
         
         if(!local_dir) {
@@ -3039,7 +2795,7 @@
 
 
 LocalResource* local_resource_new(SyncDirectory *dir, SyncDatabase *db, char *path) {
-    char *file_path = create_local_path(dir, path);
+    char *file_path = syncdir_create_local_path(dir, path);
     SYS_STAT s;
     if(sys_lstat(file_path, &s)) {
         log_error("Cannot stat file %s: %s\n", file_path, strerror(errno));
@@ -3260,7 +3016,7 @@
         
         // check if xattr have changed
         if((dir->metadata & FINFO_XATTR) == FINFO_XATTR) {
-            char *path = create_local_path(dir, local_resource_path(db_res));
+            char *path = syncdir_create_local_path(dir, local_resource_path(db_res));
             XAttributes *xattr = file_get_attributes(path, (xattr_filter_func)xattr_filter, dir);
             // test if xattr are added, removed or changed
             if((db_res->xattr_hash && !xattr) ||
@@ -3395,7 +3151,7 @@
 int local_resource_load_metadata(SyncDirectory *dir, LocalResource *res) {
     // currently only xattr needed
     if((dir->metadata & FINFO_XATTR) == FINFO_XATTR) {
-        char *path = create_local_path(dir, local_resource_path(res));
+        char *path = syncdir_create_local_path(dir, local_resource_path(res));
         XAttributes *xattr = file_get_attributes(path, (xattr_filter_func)xattr_filter, dir);
         res->xattr = xattr;
         free(path);
@@ -3728,7 +3484,7 @@
     }
     
     if(!store_tags) {
-        nullfree(local->remote_tags_hash);
+        free(local->remote_tags_hash);
         local->remote_tags_hash = remote_hash;
         return 0;
     }
@@ -3819,7 +3575,7 @@
     CxBuffer *buf = NULL;
     if(dir->tagconfig->store == TAG_STORE_XATTR) {
         ssize_t tag_length = 0;
-        char *local_path = create_local_path(dir, local_resource_path(res));
+        char *local_path = syncdir_create_local_path(dir, local_resource_path(res));
         char* tag_data = xattr_get(
                 local_path,
                 dir->tagconfig->xattr_name,
@@ -4347,7 +4103,7 @@
         LocalResource *local,
         int *counter)
 {
-    char *local_path = create_local_path(dir, local_resource_path(local));
+    char *local_path = syncdir_create_local_path(dir, local_resource_path(local));
     
     SYS_STAT s;
     if(sys_stat(local_path, &s)) {
@@ -4518,7 +4274,7 @@
         DavBool copy,
         int *counter)
 {
-    char *local_path = create_local_path(dir, local->path);
+    char *local_path = syncdir_create_local_path(dir, local->path);
     
     SYS_STAT s;
     if(sys_stat(local_path, &s)) {
@@ -4701,8 +4457,8 @@
                         free_taglist(tags);
                         tags = new_tags;
                         
-                        nullfree(tags_hash);
-                        nullfree(new_remote_hash);
+                        free(tags_hash);
+                        free(new_remote_hash);
                         tags_hash = create_tags_hash(tags);
                         new_remote_hash = nullstrdup(tags_hash);
                         
@@ -4710,7 +4466,7 @@
                     }
                 }
             }
-            nullfree(remote_hash);
+            free(remote_hash);
             
             if(dir->tagconfig->local_format == TAG_FORMAT_CSV) {
                 // csv tag lists don't have colors, so we have to add
@@ -4795,7 +4551,7 @@
     
     CxMapIterator i = cxMapIteratorValues(db->conflict);
     cx_foreach(LocalResource *, res, i) {
-        char *path = create_local_path(dir, res->path);
+        char *path = syncdir_create_local_path(dir, res->path);
         SYS_STAT s;
         if(sys_stat(path, &s)) {
             if(errno == ENOENT) {
@@ -4918,7 +4674,7 @@
     CxMapIterator i = cxMapIteratorValues(db->conflict);
     cx_foreach(LocalResource*, res, i) {
         log_printf("delete: %s\n", res->path);
-        char *path = create_local_path(dir, res->path);
+        char *path = syncdir_create_local_path(dir, res->path);
         if(sys_unlink(path)) {
             if(errno != ENOENT) {
                 log_error("unlink: %s", strerror(errno));
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/dav/syncdir.c	Tue Sep 09 16:01:30 2025 +0200
@@ -0,0 +1,41 @@
+/*
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
+ *
+ * Copyright 2025 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 "syncdir.h"
+
+#include <string.h>
+#include <libidav/utils.h>
+
+char* syncdir_create_local_path(SyncDirectory *dir, const char *path) {
+    char *local_path = util_concat_path(dir->path, path);
+    size_t local_path_len = strlen(local_path);
+    if(local_path[local_path_len-1] == '/') {
+        local_path[local_path_len-1] = '\0';
+    }
+    return local_path;
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/dav/syncdir.h	Tue Sep 09 16:01:30 2025 +0200
@@ -0,0 +1,46 @@
+/*
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
+ *
+ * Copyright 2025 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.
+ */
+
+#ifndef SYNCDIR_H
+#define SYNCDIR_H
+
+#include "sync.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+char* syncdir_create_local_path(SyncDirectory *dir, const char *path);
+
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* SYNCDIR_H */
+

mercurial