adds more tag config options

Sat, 03 Feb 2018 15:59:53 +0100

author
Olaf Wintermann <olaf.wintermann@gmail.com>
date
Sat, 03 Feb 2018 15:59:53 +0100
changeset 368
11797f33bc24
parent 367
4a6a59f89f9f
child 369
4322b8953bd5

adds more tag config options

dav/scfg.c file | annotate | diff | comparison | revisions
dav/scfg.h file | annotate | diff | comparison | revisions
dav/sync.c file | annotate | diff | comparison | revisions
dav/sync.h file | annotate | diff | comparison | revisions
--- a/dav/scfg.c	Fri Feb 02 18:57:21 2018 +0100
+++ b/dav/scfg.c	Sat Feb 03 15:59:53 2018 +0100
@@ -155,7 +155,9 @@
     conf.store = TAG_STORE_XATTR;
     conf.local_format = TAG_FORMAT_TEXT;
     conf.server_format = TAG_FORMAT_XML;
-    conf.scan = false;
+    conf.xattr_name = NULL;
+    conf.detect_changes = false;
+    conf.conflict = TAG_NO_CONFLICT;
     xmlNode *c = node->children;
     
     // TODO: error handling
@@ -184,16 +186,47 @@
                     }
                     attr = attr->next;
                 }
-            } else if(xstreq(c->name, "scan")) {
+            } else if(xstreq(c->name, "detect-changes")) {
+                if(!value) {
+                    return NULL;
+                }
+                conf.detect_changes = util_getboolean(value);
+            } else if(xstreq(c->name, "xattr-name")) {
+                if(!value) {
+                    return NULL;
+                }
+                conf.xattr_name = strdup(value);
+            } else if(xstreq(c->name, "on-conflict")) {
                 if(!value) {
                     return NULL;
                 }
-                conf.scan = util_getboolean(value);
+                if(xstreq(value, "no_conflict")) {
+                    conf.conflict = TAG_NO_CONFLICT;
+                } else if(xstreq(value, "keep_local")) {
+                    conf.conflict = TAG_KEEP_LOCAL;
+                } else if(xstreq(value, "keep_remote")) {
+                    conf.conflict = TAG_KEEP_REMOTE;
+                } else if(xstreq(value, "merge")) {
+                    conf.conflict = TAG_MERGE;
+                } else {
+                    fprintf(stderr, "on-conflict: unknown value: %s\n", value);
+                    return NULL;
+                }
             }
         }
         c = c->next;
     }
     
+    if(conf.store == TAG_STORE_XATTR && !conf.xattr_name) {
+        switch(conf.local_format) {
+            default:
+            case TAG_FORMAT_TEXT: 
+            case TAG_FORMAT_CSV: 
+            case TAG_FORMAT_XML: conf.xattr_name = strdup(DEFAULT_TAG_XATTR); break;
+            case TAG_FORMAT_MACOS: conf.xattr_name = strdup(MACOS_TAG_XATTR); break;
+        }
+    }
+    
     TagConfig *tagconfig = malloc(sizeof(TagConfig));
     *tagconfig = conf;
     return tagconfig;
--- a/dav/scfg.h	Fri Feb 02 18:57:21 2018 +0100
+++ b/dav/scfg.h	Sat Feb 03 15:59:53 2018 +0100
@@ -42,6 +42,9 @@
 #define SYNC_CMD_PUSH    2
 #define SYNC_CMD_ARCHIVE 4
     
+#define DEFAULT_TAG_XATTR "tags"
+#define MACOS_TAG_XATTR "com.apple.metadata:_kMDItemUserTags"
+    
 typedef struct TagConfig TagConfig;
     
 typedef struct SyncDirectory {
@@ -78,11 +81,21 @@
 };
 typedef enum TagStore TagStore;
 
+enum TagConflict {
+    TAG_NO_CONFLICT = 0,
+    TAG_KEEP_LOCAL,
+    TAG_KEEP_REMOTE,
+    TAG_MERGE
+};
+typedef enum TagConflict TagConflict;
+
 struct TagConfig {
     TagStore store;
     TagFormat local_format;
     TagFormat server_format;
-    bool scan;
+    char *xattr_name;
+    TagConflict conflict;
+    bool detect_changes;
 };
 
 int load_sync_config();
--- a/dav/sync.c	Fri Feb 02 18:57:21 2018 +0100
+++ b/dav/sync.c	Sat Feb 03 15:59:53 2018 +0100
@@ -974,7 +974,7 @@
                         if(dir->tagconfig && local_res->tags_updated && !abort) {
                             // get tags from tagstore (xattr or something else)
                             // and store it in resource property
-                            UcxList *tags = sync_get_file_tags(dir, local_res);
+                            UcxList *tags = sync_get_file_tags(dir, local_res, NULL);
                             DavXmlNode *prop = create_xml_taglist(tags);
                             if(prop) {
                                 dav_set_property_ns(res, DAV_NS, "tags", prop);
@@ -1248,7 +1248,7 @@
             res->etag = strdup(db_res->etag);
         }
         
-        if(dir->tagconfig && dir->tagconfig->scan && !res->tags_updated) {
+        if(dir->tagconfig && dir->tagconfig->detect_changes && !res->tags_updated) {
             UcxBuffer *tags = sync_get_file_tag_data(dir, res);
             if(tags) {
                 if(db_res->tags_hash) {
@@ -1330,34 +1330,94 @@
         }
     }
     
-    if(!tags) {
+    DavBool store_empty_tags = FALSE;
+    if(dir->tagconfig->conflict != TAG_NO_CONFLICT) {
+        DavBool tags_changed = FALSE;
+        UcxList *local_tags = sync_get_file_tags(dir, local, &tags_changed);
+        if(tags_changed) {
+            switch(dir->tagconfig->conflict) {
+                case TAG_KEEP_LOCAL: {
+                    store_empty_tags = TRUE;
+                    tags = local_tags;
+                    // TODO: free tags
+                    break;
+                }
+                case TAG_KEEP_REMOTE: break;
+                case TAG_MERGE: {
+                    // this map is used to check the existence of tags
+                    UcxMap *tag_map = ucx_map_new(32);
+                    // merged taglist
+                    UcxList *new_tags = NULL;
+                    
+                    // add all local tags
+                    UCX_FOREACH(elm, local_tags) {
+                        DavTag *t = elm->data;
+                        ucx_map_cstr_put(tag_map, t->name, t);
+                        DavTag *newt = calloc(1, sizeof(DavTag));
+                        newt->color = t->color ? strdup(t->color) : NULL;
+                        newt->name = strdup(t->name);
+                        new_tags = ucx_list_append(new_tags, newt);
+                    }
+                    // check if a remote tag is already in the map
+                    // and if not add it to the new taglist
+                    UCX_FOREACH(elm, tags) {
+                        DavTag *t = elm->data;
+                        if(!ucx_map_cstr_get(tag_map, t->name)) {
+                            DavTag *newt = calloc(1, sizeof(DavTag));
+                            newt->color = t->color ? strdup(t->color) : NULL;
+                            newt->name = strdup(t->name);
+                            new_tags = ucx_list_append(new_tags, newt);
+                        }
+                    }
+                    
+                    ucx_map_free(tag_map);
+                    // TODO: free tags and local_tags
+                    
+                    tags = new_tags;
+                    
+                    store_empty_tags = TRUE;
+                    
+                    break;
+                }
+            }
+        } else {
+            // TODO: free local_tags
+        }
+    }
+    
+    if(!tags && !store_empty_tags) {
         return 0;
     }
     
     int ret = 0;
     if(dir->tagconfig->store == TAG_STORE_XATTR) {
         UcxBuffer *data = NULL;
-        switch(dir->tagconfig->local_format) {
-            default: break;
-            case TAG_FORMAT_TEXT: {
-                data = create_text_taglist(tags);
-                break;
-            }
-            case TAG_FORMAT_CSV: {
-                data = create_csv_taglist(tags);
-                break;
+        if(tags) {
+            switch(dir->tagconfig->local_format) {
+                default: break;
+                case TAG_FORMAT_TEXT: {
+                    data = create_text_taglist(tags);
+                    break;
+                }
+                case TAG_FORMAT_CSV: {
+                    data = create_csv_taglist(tags);
+                    break;
+                }
+                case TAG_FORMAT_MACOS: {
+                    data = create_macos_taglist(tags);
+                    break;
+                }
             }
-            case TAG_FORMAT_MACOS: {
-                data = create_macos_taglist(tags);
-                break;
+            
+            if(data) {
+                ret = xattr_set(path, dir->tagconfig->xattr_name, data->space, data->pos);
+                ucx_buffer_free(data);
+            } else {
+                ret = -1;
             }
-        }
-        
-        if(data) {
-            ret = xattr_set(path, "tags", data->space, data->pos);
-            ucx_buffer_free(data);
         } else {
-            ret = -1;
+            // TODO: relete xattr
+            //ret = xattr_remove(path, dir->tagconfig->xattr_name);
         }
     }
     
@@ -1376,7 +1436,7 @@
         char *local_path = util_concat_path(dir->path, res->path);
         char* tag_data = xattr_get(
                 local_path,
-                dir->tagconfig->local_format == TAG_FORMAT_MACOS ? MACOS_TAG_XATTR : "tags",
+                dir->tagconfig->xattr_name,
                 &tag_length);
         free(local_path);
         
@@ -1388,7 +1448,9 @@
     return buf;
 }
 
-UcxList* sync_get_file_tags(SyncDirectory *dir, LocalResource *res) {
+UcxList* sync_get_file_tags(SyncDirectory *dir, LocalResource *res, DavBool *changed) {
+    if(changed) *changed = FALSE;
+    
     UcxList *tags = NULL;
     
     if(!dir->tagconfig) {
@@ -1400,7 +1462,16 @@
                 sync_get_file_tag_data(dir, res);
         
         if(tag_buf) {
-            res->tags_hash = dav_create_hash(tag_buf->space, tag_buf->size);
+            char *newhash = dav_create_hash(tag_buf->space, tag_buf->size);
+            if(res->tags_hash) {
+                if(changed && strcmp(res->tags_hash, newhash)) {
+                    *changed = TRUE;
+                }
+                free(res->tags_hash);
+            } else {
+                if(changed) *changed = TRUE;
+            }
+            res->tags_hash = newhash;
             
             switch(dir->tagconfig->local_format) {
                 default: break;
@@ -1421,6 +1492,8 @@
                 // we created the buffer therefore we destroy it
                 ucx_buffer_free(tag_buf);
             }
+        } else if(res->tags_hash) {
+            if(changed) *changed = TRUE;
         }
     }
     
@@ -1453,7 +1526,7 @@
     dav_set_content(res, in, (dav_read_func)fread);
     
     if(dir->tagconfig) {
-        UcxList *tags = sync_get_file_tags(dir, local);
+        UcxList *tags = sync_get_file_tags(dir, local, NULL); // TODO: check remote tags
         DavXmlNode *prop = create_xml_taglist(tags);
         if(prop) {
             dav_set_property_ns(res, DAV_NS, "tags", prop);
--- a/dav/sync.h	Fri Feb 02 18:57:21 2018 +0100
+++ b/dav/sync.h	Sat Feb 03 15:59:53 2018 +0100
@@ -50,8 +50,6 @@
 #endif
 
 #define STDIN_BUF_SIZE 2048
-    
-#define MACOS_TAG_XATTR "com.apple.metadata:_kMDItemUserTags"
 
 void print_usage(char *cmd);
 
@@ -86,7 +84,7 @@
 int sync_set_status(DavResource *res, char *status);
 int sync_remove_status(DavResource *res);
 UcxBuffer* sync_get_file_tag_data(SyncDirectory *dir, LocalResource *res);
-UcxList* sync_get_file_tags(SyncDirectory *dir, LocalResource *res);
+UcxList* sync_get_file_tags(SyncDirectory *dir, LocalResource *res, DavBool *changed);
 int sync_store_tags(SyncDirectory *dir, const char *path, LocalResource *local, DavResource *res);
 int sync_put_resource(
         SyncDirectory *dir,

mercurial