Tue, 09 Sep 2025 16:01:30 +0200
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 */ +