dav/sync.c

changeset 525
26a1d5b9d9d2
parent 524
d53fd1006485
child 526
e3c0440bd599
equal deleted inserted replaced
524:d53fd1006485 525:26a1d5b9d9d2
67 static void xmlerrorfnc(void * c, const char * msg, ... ) { 67 static void xmlerrorfnc(void * c, const char * msg, ... ) {
68 va_list ap; 68 va_list ap;
69 va_start(ap, msg); 69 va_start(ap, msg);
70 vfprintf(stderr, msg, ap); 70 vfprintf(stderr, msg, ap);
71 va_end(ap); 71 va_end(ap);
72 }
73
74 /*
75 * strcmp version that works with NULL pointers
76 */
77 static int nullstrcmp(const char *s1, const char *s2) {
78 if(!s1 && s2) {
79 return -1;
80 }
81 if(s1 && s2) {
82 return 1;
83 }
84 if(!s1 && !s2) {
85 return 0;
86 }
87 return strcmp(s1, s2);
72 } 88 }
73 89
74 int main(int argc, char **argv) { 90 int main(int argc, char **argv) {
75 if(argc < 2) { 91 if(argc < 2) {
76 fprintf(stderr, "Missing command\n"); 92 fprintf(stderr, "Missing command\n");
309 if(res->isdirectory) { 325 if(res->isdirectory) {
310 return 1; 326 return 1;
311 } 327 }
312 328
313 DavBool changed = 0; 329 DavBool changed = 0;
314 UcxList *res_tags = sync_get_file_tags(dir, res, &changed); 330 UcxList *res_tags = sync_get_file_tags(dir, res, &changed, NULL);
315 331
316 int ret = matches_tagfilter(res_tags, tagfilter); 332 int ret = matches_tagfilter(res_tags, tagfilter);
317 UCX_FOREACH(elm, res_tags) { 333 UCX_FOREACH(elm, res_tags) {
318 DavTag *t = elm->data; 334 DavTag *t = elm->data;
319 free_dav_tag(t); 335 free_dav_tag(t);
1317 sync_error++; 1333 sync_error++;
1318 error = 1; 1334 error = 1;
1319 abort = 1; 1335 abort = 1;
1320 } 1336 }
1321 1337
1322 if(dir->tagconfig && local_res->tags_updated && !abort) { 1338 if(local_res->metadata_updated && !abort) {
1323 sync_update_metadata(dir, sn, res, local_res); 1339 sync_update_metadata(dir, sn, res, local_res);
1324 } 1340 }
1325 } else if(sn->error != DAV_OK) { 1341 } else if(sn->error != DAV_OK) {
1326 // dav_exists() failed 1342 // dav_exists() failed
1327 print_resource_error(sn, local_res->path); 1343 print_resource_error(sn, local_res->path);
1328 ret = -1; 1344 ret = -1;
1329 sync_error++; 1345 sync_error++;
1330 error = 1; 1346 error = 1;
1331 } 1347 }
1332 } else { 1348 } else {
1333 if(cdt && remote_resource_is_changed(sn, dir, db, local_res)) { 1349 if(cdt && remote_resource_is_changed(sn, dir, db, res, local_res)) {
1334 printf("conflict: %s\n", local_res->path); 1350 printf("conflict: %s\n", local_res->path);
1335 local_res->last_modified = 0; 1351 local_res->last_modified = 0;
1336 local_res->skipped = TRUE; 1352 local_res->skipped = TRUE;
1337 sync_skipped++; 1353 sync_skipped++;
1338 } else { 1354 } else {
1355 // metadata updates 1371 // metadata updates
1356 for(UcxList *elm=ls_update;elm && !sync_shutdown;elm=elm->next) { 1372 for(UcxList *elm=ls_update;elm && !sync_shutdown;elm=elm->next) {
1357 LocalResource *local_res = elm->data; 1373 LocalResource *local_res = elm->data;
1358 1374
1359 DavResource *res = dav_resource_new(sn, local_res->path); 1375 DavResource *res = dav_resource_new(sn, local_res->path);
1376 if(dir->tagconfig) {
1377 DavPropName properties[] = {
1378 {DAV_NS,"tags"},
1379 };
1380 if(dav_load_prop(res, properties, 1)) {
1381 sync_error++;
1382 print_resource_error(sn, res->path);
1383 ret = -1;
1384 error = 1;
1385 continue;
1386 }
1387 }
1360 1388
1361 if(local_res->metadata_updated) { 1389 if(local_res->metadata_updated) {
1362 if(!sync_update_metadata(dir, sn, res, local_res)) { 1390 if(!sync_update_metadata(dir, sn, res, local_res)) {
1363 LocalResource *dbres = ucx_map_cstr_remove(db->resources, local_res->path); 1391 LocalResource *dbres = ucx_map_cstr_remove(db->resources, local_res->path);
1364 ucx_map_cstr_put(db->resources, local_res->path, local_res); 1392 ucx_map_cstr_put(db->resources, local_res->path, local_res);
1762 res->etag = strdup(db_res->etag); 1790 res->etag = strdup(db_res->etag);
1763 } 1791 }
1764 if(db_res->tags_hash) { 1792 if(db_res->tags_hash) {
1765 res->tags_hash = strdup(db_res->tags_hash); 1793 res->tags_hash = strdup(db_res->tags_hash);
1766 } 1794 }
1795 if(db_res->remote_tags_hash) {
1796 res->remote_tags_hash = strdup(db_res->remote_tags_hash);
1797 }
1798 if(db_res->xattr_hash) {
1799 res->xattr_hash = strdup(db_res->xattr_hash);
1800 }
1767 1801
1768 if(dir->tagconfig && dir->tagconfig->detect_changes && !res->tags_updated) { 1802 if(dir->tagconfig && dir->tagconfig->detect_changes && !res->tags_updated) {
1769 UcxBuffer *tags = sync_get_file_tag_data(dir, res); 1803 UcxBuffer *tags = sync_get_file_tag_data(dir, res);
1770 if(tags) { 1804 if(tags) {
1771 if(db_res->tags_hash) { 1805 if(db_res->tags_hash) {
1821 1855
1822 int remote_resource_is_changed( 1856 int remote_resource_is_changed(
1823 DavSession *sn, 1857 DavSession *sn,
1824 SyncDirectory *dir, 1858 SyncDirectory *dir,
1825 SyncDatabase *db, 1859 SyncDatabase *db,
1860 DavResource *remote,
1826 LocalResource *res) 1861 LocalResource *res)
1827 { 1862 {
1863 DavPropName properties[] = {
1864 {"DAV:","getetag"},
1865 {DAV_NS,"tags"},
1866 {DAV_NS,VERSION_PATH_PROPERTY}
1867 };
1868 int err = dav_load_prop(remote, properties, 3);
1869
1828 if(res->restore) { 1870 if(res->restore) {
1829 return 0; 1871 return 0;
1830 } 1872 }
1831 1873
1832 DavResource *remote = dav_get(sn, res->path, "D:getetag");
1833 int ret = 0; 1874 int ret = 0;
1834 if(remote) { 1875 if(err == 0) {
1835 char *etag = dav_get_string_property(remote, "D:getetag"); 1876 char *etag = dav_get_string_property(remote, "D:getetag");
1836 if(!res->etag) { 1877 if(!res->etag) {
1837 // the resource is on the server and the client has no etag 1878 // the resource is on the server and the client has no etag
1838 ret = 1; 1879 ret = 1;
1839 } else if(etag) { 1880 } else if(etag) {
1846 } 1887 }
1847 } else { 1888 } else {
1848 // something weird is happening, the server must support etags 1889 // something weird is happening, the server must support etags
1849 fprintf(stderr, "Warning: resource %s has no etag\n", remote->href); 1890 fprintf(stderr, "Warning: resource %s has no etag\n", remote->href);
1850 } 1891 }
1851 dav_resource_free(remote);
1852 } 1892 }
1853 return ret; 1893 return ret;
1854 } 1894 }
1855 1895
1856 int resource_pathlen_cmp(LocalResource *res1, LocalResource *res2, void *n) { 1896 int resource_pathlen_cmp(LocalResource *res1, LocalResource *res2, void *n) {
1878 DavResource *resource = dav_resource_new(res->session, res->path); 1918 DavResource *resource = dav_resource_new(res->session, res->path);
1879 dav_remove_property(resource, "idav:status"); 1919 dav_remove_property(resource, "idav:status");
1880 int ret = dav_store(resource); 1920 int ret = dav_store(resource);
1881 dav_resource_free(resource); 1921 dav_resource_free(resource);
1882 return ret; 1922 return ret;
1883 }
1884
1885 UcxList* sync_merge_tags(UcxList *tags1, UcxList *tags2) {
1886 // this map is used to check the existence of tags
1887 UcxMap *tag_map = ucx_map_new(32);
1888 // merged taglist
1889 UcxList *new_tags = NULL;
1890
1891 // add all local tags
1892 UCX_FOREACH(elm, tags1) {
1893 DavTag *t = elm->data;
1894 ucx_map_cstr_put(tag_map, t->name, t);
1895 DavTag *newt = calloc(1, sizeof(DavTag));
1896 newt->color = t->color ? strdup(t->color) : NULL;
1897 newt->name = strdup(t->name);
1898 new_tags = ucx_list_append(new_tags, newt);
1899 }
1900 // check if a remote tag is already in the map
1901 // and if not add it to the new taglist
1902 UCX_FOREACH(elm, tags2) {
1903 DavTag *t = elm->data;
1904 if(!ucx_map_cstr_get(tag_map, t->name)) {
1905 DavTag *newt = calloc(1, sizeof(DavTag));
1906 newt->color = t->color ? strdup(t->color) : NULL;
1907 newt->name = strdup(t->name);
1908 new_tags = ucx_list_append(new_tags, newt);
1909 }
1910 }
1911
1912 ucx_map_free(tag_map);
1913
1914 return new_tags;
1915 } 1923 }
1916 1924
1917 int sync_tags_equal(UcxList *tags1, UcxList *tags2) { 1925 int sync_tags_equal(UcxList *tags1, UcxList *tags2) {
1918 if(!tags1) { 1926 if(!tags1) {
1919 return tags2 ? 0 : 1; 1927 return tags2 ? 0 : 1;
1960 } 1968 }
1961 } 1969 }
1962 1970
1963 DavBool store_tags = FALSE; 1971 DavBool store_tags = FALSE;
1964 DavBool tags_changed = FALSE; 1972 DavBool tags_changed = FALSE;
1965 UcxList *local_tags = sync_get_file_tags(dir, local, &tags_changed); 1973 UcxList *local_tags = sync_get_file_tags(dir, local, &tags_changed, NULL);
1966 if(tags_changed) { 1974 if(tags_changed) {
1967 switch(dir->tagconfig->conflict) { 1975 switch(dir->tagconfig->conflict) {
1968 case TAG_NO_CONFLICT: 1976 case TAG_NO_CONFLICT:
1969 case TAG_KEEP_LOCAL: { 1977 case TAG_KEEP_LOCAL: {
1970 store_tags = FALSE; 1978 store_tags = FALSE;
1973 case TAG_KEEP_REMOTE: { 1981 case TAG_KEEP_REMOTE: {
1974 store_tags = TRUE; 1982 store_tags = TRUE;
1975 local->tags_updated = FALSE; 1983 local->tags_updated = FALSE;
1976 } 1984 }
1977 case TAG_MERGE: { 1985 case TAG_MERGE: {
1978 UcxList *new_tags = sync_merge_tags(local_tags, tags); 1986 UcxList *new_tags = merge_tags(local_tags, tags);
1979 // TODO: free tags and local_tags 1987 // TODO: free tags and local_tags
1980 tags = new_tags; 1988 tags = new_tags;
1981 store_tags = TRUE; 1989 store_tags = TRUE;
1982 // make sure the merged tags will be pushed the next time 1990 // make sure the merged tags will be pushed the next time
1983 local->tags_updated = TRUE; 1991 local->tags_updated = TRUE;
2083 } 2091 }
2084 res->cached_tags = buf; 2092 res->cached_tags = buf;
2085 return buf; 2093 return buf;
2086 } 2094 }
2087 2095
2088 UcxList* sync_get_file_tags(SyncDirectory *dir, LocalResource *res, DavBool *changed) { 2096 UcxList* sync_get_file_tags(SyncDirectory *dir, LocalResource *res, DavBool *changed, char **newhash) {
2089 if(changed) *changed = FALSE; 2097 if(changed) *changed = FALSE;
2090 2098
2091 UcxList *tags = NULL; 2099 UcxList *tags = NULL;
2092 2100
2093 if(!res) { 2101 if(!res) {
2104 UcxBuffer *tag_buf = res->cached_tags ? 2112 UcxBuffer *tag_buf = res->cached_tags ?
2105 res->cached_tags : 2113 res->cached_tags :
2106 sync_get_file_tag_data(dir, res); 2114 sync_get_file_tag_data(dir, res);
2107 2115
2108 if(tag_buf) { 2116 if(tag_buf) {
2109 char *newhash = dav_create_hash(tag_buf->space, tag_buf->size); 2117 char *new_hash = dav_create_hash(tag_buf->space, tag_buf->size);
2110 if(res->tags_hash) { 2118 if(res->tags_hash) {
2111 if(changed && strcmp(res->tags_hash, newhash)) { 2119 if(changed && strcmp(res->tags_hash, new_hash)) {
2112 *changed = TRUE; 2120 *changed = TRUE;
2113 } 2121 }
2114 free(res->tags_hash); 2122 free(res->tags_hash);
2115 } else { 2123 } else {
2116 if(changed) *changed = TRUE; 2124 if(changed) *changed = TRUE;
2117 } 2125 }
2118 res->tags_hash = newhash; 2126 if(!newhash) {
2127 *newhash = new_hash;
2128 } else {
2129 free(newhash);
2130 }
2119 2131
2120 switch(dir->tagconfig->local_format) { 2132 switch(dir->tagconfig->local_format) {
2121 default: break; 2133 default: break;
2122 case TAG_FORMAT_TEXT: { 2134 case TAG_FORMAT_TEXT: {
2123 tags = parse_text_taglist(tag_buf->space, tag_buf->size); 2135 tags = parse_text_taglist(tag_buf->space, tag_buf->size);
2164 2176
2165 #define VBEGIN_ERROR_MKCOL 1 2177 #define VBEGIN_ERROR_MKCOL 1
2166 #define VBEGIN_ERROR_MOVE 2 2178 #define VBEGIN_ERROR_MOVE 2
2167 #define VBEGIN_ERROR_PROPPATCH 3 2179 #define VBEGIN_ERROR_PROPPATCH 3
2168 #define VBEGIN_ERROR_CHECKOUT 4 2180 #define VBEGIN_ERROR_CHECKOUT 4
2169 int versioning_begin(SyncDirectory *dir, DavResource *res, int *exists) { 2181 int versioning_begin(SyncDirectory *dir, DavResource *res) {
2170 int ret = 0; 2182 int ret = 0;
2171 2183
2172 if(dir->versioning->type == VERSIONING_SIMPLE && *exists) { 2184 if(dir->versioning->type == VERSIONING_SIMPLE && res->exists) {
2173 DavResource *history_collection = dav_resource_new( 2185 DavResource *history_collection = dav_resource_new(
2174 res->session, 2186 res->session,
2175 dir->versioning->collection); 2187 dir->versioning->collection);
2176 2188
2177 // get the path to the version history collection for this resource 2189 // get the path to the version history collection for this resource
2178 // if propfind fails we just assume that it doesn't exist 2190 // if propfind fails we just assume that it doesn't exist
2179 // better error handling is done later (sync_put_resource) 2191 // better error handling is done later (sync_put_resource)
2180 // if there is no history collection for this resource, we create one 2192 // if there is no history collection for this resource, we create one
2181
2182 DavPropName prop;
2183 prop.ns = DAV_NS;
2184 prop.name = VERSION_PATH_PROPERTY;
2185 *exists = dav_load_prop(res, &prop, 1);
2186 2193
2187 char *history_href = NULL; 2194 char *history_href = NULL;
2188 char *vcol_path = dav_get_string_property_ns(res, DAV_NS, VERSION_PATH_PROPERTY); 2195 char *vcol_path = dav_get_string_property_ns(res, DAV_NS, VERSION_PATH_PROPERTY);
2189 if(!vcol_path) { 2196 if(!vcol_path) {
2190 DavResource *history_res = NULL; 2197 DavResource *history_res = NULL;
2287 // TODO 2294 // TODO
2288 } 2295 }
2289 return 0; 2296 return 0;
2290 } 2297 }
2291 2298
2299 static void update_metadata_hashes(LocalResource *local, MetadataHashes hashes) {
2300 if(hashes.tags) {
2301 if(local->tags_hash) {
2302 free(local->tags_hash);
2303 }
2304 local->tags_hash = hashes.tags;
2305 }
2306 if(hashes.tags_remote) {
2307 if(local->remote_tags_hash) {
2308 free(local->remote_tags_hash);
2309 }
2310 local->remote_tags_hash = hashes.tags_remote;
2311 }
2312 if(hashes.xattr) {
2313 if(local->xattr_hash) {
2314 free(local->xattr_hash);
2315 }
2316 local->xattr_hash = hashes.xattr;
2317 }
2318 }
2319
2292 int sync_put_resource( 2320 int sync_put_resource(
2293 SyncDirectory *dir, 2321 SyncDirectory *dir,
2294 DavResource *res, 2322 DavResource *res,
2295 LocalResource *local, 2323 LocalResource *local,
2296 int *counter) 2324 int *counter)
2313 } 2341 }
2314 2342
2315 dav_set_content(res, in, (dav_read_func)myread, (dav_seek_func)file_seek); 2343 dav_set_content(res, in, (dav_read_func)myread, (dav_seek_func)file_seek);
2316 dav_set_content_length(res, s.st_size); 2344 dav_set_content_length(res, s.st_size);
2317 2345
2318 if(dir->tagconfig) { 2346 MetadataHashes hashes;
2319 UcxList *tags = sync_get_file_tags(dir, local, NULL); 2347 hashes = sync_set_metadata_properties(dir, res->session, res, local);
2320 DavXmlNode *prop = create_xml_taglist(tags); 2348
2321 if(prop) { 2349 // before sync_put_resource, remote_resource_is_changed does a propfind
2322 dav_set_property_ns(res, DAV_NS, "tags", prop); 2350 // and sets res->exists
2323 } 2351 int exists = res->exists;
2324 }
2325
2326 int exists = dav_exists(res);
2327 if(dir->versioning && dir->versioning->always) { 2352 if(dir->versioning && dir->versioning->always) {
2328 int err = versioning_begin(dir, res, &exists); 2353 int err = versioning_begin(dir, res);
2329 if(err) { 2354 if(err) {
2330 fprintf(stderr, "Cannot store version for resource: %s\n", res->href); 2355 fprintf(stderr, "Cannot store version for resource: %s\n", res->href);
2331 free(local_path); 2356 free(local_path);
2332 return -1; 2357 return -1;
2333 } 2358 }
2334 } else {
2335 exists = dav_exists(res);
2336 } 2359 }
2337 2360
2338 int ret = -1; 2361 int ret = -1;
2339 for(int i=0;i<=dir->max_retry;i++) { 2362 for(int i=0;i<=dir->max_retry;i++) {
2340 if(!exists && dav_create(res)) { 2363 if(!exists && dav_create(res)) {
2355 } 2378 }
2356 } 2379 }
2357 2380
2358 if(ret == 0) { 2381 if(ret == 0) {
2359 (*counter)++; 2382 (*counter)++;
2383
2384 update_metadata_hashes(local, hashes);
2360 2385
2361 // check contentlength and get new etag 2386 // check contentlength and get new etag
2362 DavResource *up_res = dav_get(res->session, res->path, "D:getetag,idav:status,idav:tags"); 2387 DavResource *up_res = dav_get(res->session, res->path, "D:getetag,idav:status,idav:tags");
2363 2388
2364 if(up_res) { 2389 if(up_res) {
2481 dav_resource_free(res); 2506 dav_resource_free(res);
2482 2507
2483 return ret; 2508 return ret;
2484 } 2509 }
2485 2510
2486 int sync_update_metadata(SyncDirectory *dir, DavSession *sn, DavResource *res, LocalResource *local) { 2511 MetadataHashes sync_set_metadata_properties(
2487 if(dir->tagconfig && local->tags_updated) { 2512 SyncDirectory *dir,
2513 DavSession *sn,
2514 DavResource *res,
2515 LocalResource *local)
2516 {
2517 MetadataHashes hashes = {NULL, NULL, NULL};
2518 if(dir->tagconfig) {
2488 // get local tags 2519 // get local tags
2489 UcxList *tags = sync_get_file_tags(dir, local, NULL); 2520 DavBool changed = 0;
2490 2521 char *tags_hash = NULL;
2491 DavXmlNode *prop = create_xml_taglist(tags); 2522 UcxList *tags = sync_get_file_tags(dir, local, &changed, &tags_hash);
2492 if(prop) { 2523 if(changed || local->tags_updated) {
2493 dav_set_property_ns(res, DAV_NS, "tags", prop); 2524 hashes.tags = tags_hash;
2525
2526 DavBool store_tags = TRUE;
2527 // get remote tags
2528 UcxList *remote_tags = NULL;
2529 DavXmlNode *tagsprop = dav_get_property_ns(res, DAV_NS, "tags");
2530 if(tagsprop) {
2531 remote_tags = parse_dav_xml_taglist(tagsprop);
2532 }
2533 char *remote_hash = create_tags_hash(remote_tags);
2534 if(nullstrcmp(remote_hash, local->remote_tags_hash)) {
2535 // the tags have changed on the server
2536 switch(dir->tagconfig->conflict) {
2537 case TAG_NO_CONFLICT: break;
2538 case TAG_KEEP_LOCAL: break;
2539 case TAG_KEEP_REMOTE: {
2540 store_tags = FALSE;
2541 local->tags_updated = FALSE;
2542 break;
2543 }
2544 case TAG_MERGE: {
2545 UcxList *new_tags = merge_tags(tags, remote_tags);
2546 free_taglist(tags);
2547 tags = new_tags;
2548 break;
2549 }
2550 }
2551 }
2552
2553 if(dir->tagconfig->local_format == TAG_FORMAT_CSV) {
2554 // csv tag lists don't have colors, so we have to add
2555 // the colors from the remote tag list
2556 add_tag_colors(tags, remote_tags);
2557 }
2558
2559 if(store_tags) {
2560 if(tags) {
2561 DavXmlNode *tagprop = create_xml_taglist(tags);
2562 dav_set_property_ns(res, DAV_NS, "tags", tagprop);
2563 } else {
2564 dav_remove_property_ns(res, DAV_NS, "tags");
2565 }
2566 }
2567
2568 free_taglist(remote_tags);
2494 } else { 2569 } else {
2495 dav_remove_property_ns(res, DAV_NS, "tags"); 2570 if(tags_hash) {
2496 } 2571 free(tags_hash);
2572 }
2573 }
2574 free_taglist(tags);
2497 } 2575 }
2498 2576
2499 if(local->finfo_updated) { 2577 if(local->finfo_updated) {
2500 struct stat s; 2578 struct stat s;
2501 s.st_mode = local->mode; 2579 s.st_mode = local->mode;
2506 } 2584 }
2507 2585
2508 if(local->xattr_updated) { 2586 if(local->xattr_updated) {
2509 if(local->xattr) { 2587 if(local->xattr) {
2510 resource_set_xattr(res, local->xattr); 2588 resource_set_xattr(res, local->xattr);
2589 hashes.xattr = strdup(local->xattr->hash);
2511 } else { 2590 } else {
2512 dav_remove_property(res, "idav:xattributes"); 2591 dav_remove_property(res, "idav:xattributes");
2513 } 2592 }
2514 } 2593 }
2594 return hashes;
2595 }
2596
2597 int sync_update_metadata(
2598 SyncDirectory *dir,
2599 DavSession *sn,
2600 DavResource *res,
2601 LocalResource *local)
2602 {
2603 MetadataHashes hashes = sync_set_metadata_properties(dir, sn, res, local);
2515 2604
2516 int err = 0; 2605 int err = 0;
2517 printf("update: %s\n", local->path); 2606 printf("update: %s\n", local->path);
2518 if(dav_store(res)) { 2607 if(dav_store(res)) {
2519 print_resource_error(sn, local->path); 2608 print_resource_error(sn, local->path);
2520 err = 1; 2609 err = 1;
2521 } else { 2610 } else {
2522 if(dir->tagconfig && local->tags_updated) { 2611 update_metadata_hashes(local, hashes);
2523 UcxBuffer *tag_data = local->cached_tags; 2612 }
2524 if(local->tags_hash) { 2613
2525 free(local->tags_hash);
2526 local->tags_hash = NULL;
2527 }
2528 if(tag_data) {
2529 char *hash = dav_create_hash(tag_data->space, tag_data->size);
2530 local->tags_hash = hash;
2531 }
2532 local->tags_updated = FALSE;
2533 }
2534 if(local->xattr) {
2535 local->xattr_hash = strdup(local->xattr->hash);
2536 }
2537 }
2538
2539 // TODO: free stuff
2540 return err; 2614 return err;
2541 } 2615 }
2542 2616
2543 void remove_deleted_conflicts(SyncDirectory *dir, SyncDatabase *db) { 2617 void remove_deleted_conflicts(SyncDirectory *dir, SyncDatabase *db) {
2544 char **dc = calloc(sizeof(void*), db->conflict->count); 2618 char **dc = calloc(sizeof(void*), db->conflict->count);
2893 int cmd_add_tag(CmdArgs *args) { 2967 int cmd_add_tag(CmdArgs *args) {
2894 if(args->argc != 2) { 2968 if(args->argc != 2) {
2895 fprintf(stderr, "Too %s arguments\n", args->argc <= 1 ? "few" : "many"); 2969 fprintf(stderr, "Too %s arguments\n", args->argc <= 1 ? "few" : "many");
2896 return -1; 2970 return -1;
2897 } 2971 }
2898 return cmd_tagopt(args, CMD_TAG_ADD); 2972 return cmd_tagop(args, CMD_TAG_ADD);
2899 } 2973 }
2900 2974
2901 int cmd_remove_tag(CmdArgs *args) { 2975 int cmd_remove_tag(CmdArgs *args) {
2902 if(args->argc != 2) { 2976 if(args->argc != 2) {
2903 fprintf(stderr, "Too %s arguments\n", args->argc <= 1 ? "few" : "many"); 2977 fprintf(stderr, "Too %s arguments\n", args->argc <= 1 ? "few" : "many");
2904 return -1; 2978 return -1;
2905 } 2979 }
2906 return cmd_tagopt(args, CMD_TAG_REMOVE); 2980 return cmd_tagop(args, CMD_TAG_REMOVE);
2907 } 2981 }
2908 2982
2909 int cmd_set_tags(CmdArgs *args) { 2983 int cmd_set_tags(CmdArgs *args) {
2910 if(args->argc < 1 || args->argc > 2) { 2984 if(args->argc < 1 || args->argc > 2) {
2911 fprintf(stderr, "Too %s arguments\n", args->argc < 1 ? "few" : "many"); 2985 fprintf(stderr, "Too %s arguments\n", args->argc < 1 ? "few" : "many");
2912 return -1; 2986 return -1;
2913 } 2987 }
2914 return cmd_tagopt(args, CMD_TAG_SET); 2988 return cmd_tagop(args, CMD_TAG_SET);
2915 } 2989 }
2916 2990
2917 int cmd_list_tags(CmdArgs *args) { 2991 int cmd_list_tags(CmdArgs *args) {
2918 if(args->argc != 1) { 2992 if(args->argc != 1) {
2919 fprintf(stderr, "Too %s arguments\n", args->argc <= 1 ? "few" : "many"); 2993 fprintf(stderr, "Too %s arguments\n", args->argc <= 1 ? "few" : "many");
2920 return -1; 2994 return -1;
2921 } 2995 }
2922 return cmd_tagopt(args, CMD_TAG_LIST); 2996 return cmd_tagop(args, CMD_TAG_LIST);
2923 } 2997 }
2924 2998
2925 int cmd_tagopt(CmdArgs *args, int cmd) { 2999 int cmd_tagop(CmdArgs *args, int cmd) {
2926 SyncFile file; 3000 SyncFile file;
2927 int ret = 0; 3001 int ret = 0;
2928 char *path = args->argv[0]; 3002 char *path = args->argv[0];
2929 3003
2930 int err = sync_get_file(args, path, NULL, &file); 3004 int err = sync_get_file(args, path, NULL, &file);
2950 3024
2951 if(cmd != CMD_TAG_SET) { 3025 if(cmd != CMD_TAG_SET) {
2952 char *tag = args->argv[1]; 3026 char *tag = args->argv[1];
2953 char *tagcolor = NULL; // TODO: get color 3027 char *tagcolor = NULL; // TODO: get color
2954 3028
2955 tags = sync_get_file_tags(file.dir, localres, NULL); 3029 tags = sync_get_file_tags(file.dir, localres, NULL, NULL);
2956 UcxList *x = NULL; 3030 UcxList *x = NULL;
2957 UCX_FOREACH(elm, tags) { 3031 UCX_FOREACH(elm, tags) {
2958 DavTag *t = elm->data; 3032 DavTag *t = elm->data;
2959 if(cmd == CMD_TAG_LIST) { 3033 if(cmd == CMD_TAG_LIST) {
2960 printf("%s\n", t->name); 3034 printf("%s\n", t->name);

mercurial