diff --git a/src/clipmenud.c b/src/clipmenud.c index 81de0c9..c834c4a 100644 --- a/src/clipmenud.c +++ b/src/clipmenud.c @@ -218,7 +218,9 @@ static uint64_t store_clip(char *text) { dbg("Possible partial of last clip, replacing\n"); expect(cs_replace(&cs, CS_ITER_NEWEST_FIRST, 0, text, &hash) == 0); } else { - expect(cs_add(&cs, text, &hash) == 0); + expect(cs_add(&cs, text, &hash, + cfg.deduplicate ? CS_DUPE_KEEP_LAST : CS_DUPE_KEEP_ALL) == + 0); } if (last_text) { diff --git a/src/config.c b/src/config.c index c55fd60..3da19b9 100644 --- a/src/config.c +++ b/src/config.c @@ -269,6 +269,8 @@ int config_setup_internal(FILE *file, struct config *cfg) { {"max_clips_batch", "CM_MAX_CLIPS_BATCH", &cfg->max_clips_batch, convert_positive_int, "100", 0}, {"oneshot", "CM_ONESHOT", &cfg->oneshot, convert_positive_int, "0", 0}, + {"deduplicate", "CM_DEDUPLICATE", &cfg->deduplicate, convert_bool, "0", + 0}, {"own_clipboard", "CM_OWN_CLIPBOARD", &cfg->own_clipboard, convert_bool, "0", 0}, {"selections", "CM_SELECTIONS", &cfg->selections, convert_selections, diff --git a/src/config.h b/src/config.h index a1d994f..182ee37 100644 --- a/src/config.h +++ b/src/config.h @@ -43,6 +43,7 @@ struct config { int max_clips; int max_clips_batch; int oneshot; + bool deduplicate; bool own_clipboard; struct selection *owned_selections; struct selection *selections; diff --git a/src/store.c b/src/store.c index 7ba2b71..a76f3bf 100644 --- a/src/store.c +++ b/src/store.c @@ -527,28 +527,53 @@ int cs_content_get(struct clip_store *cs, uint64_t hash, return 0; } +/** + * Move the entry with the specified hash to the newest slot. + * + * @cs: The clip store to operate on + * @hash: The hash of the entry to move + */ +static int cs_make_newest(struct clip_store *cs, uint64_t hash) { + _drop_(cs_unref) struct ref_guard guard = cs_ref(cs); + if (guard.status < 0) { + return guard.status; + } + + for (int i = 0; i < (int)cs->local_nr_snips; ++i) { + if (cs->snips[i].hash == hash) { + struct cs_snip tmp = cs->snips[i]; + memmove(cs->snips + i, cs->snips + i + 1, + (cs->local_nr_snips - (i + 1)) * sizeof(*cs->snips)); + cs->snips[cs->local_nr_snips - 1] = tmp; + return 0; + } + } + die("unreachable"); +} + /** * Add a new content entry to the clip store and content directory. * * @cs: The clip store to operate on * @content: The content to add * @out_hash: Output for the generated hash, or NULL + * @dupe_policy: Policy to use for duplicate entries */ -int cs_add(struct clip_store *cs, const char *content, uint64_t *out_hash) { +int cs_add(struct clip_store *cs, const char *content, uint64_t *out_hash, + enum cs_dupe_policy dupe_policy) { uint64_t hash = djb64_hash(content); char line[CS_SNIP_LINE_SIZE]; size_t nr_lines = first_line(content, line); - int ret = cs_content_add(cs, hash, content, CS_DUPE_KEEP_ALL); - if (ret < 0) { - return ret; - } - if (out_hash) { *out_hash = hash; } - return cs_snip_add(cs, hash, line, nr_lines); + int ret = cs_content_add(cs, hash, content, dupe_policy); + if (ret == -EEXIST && dupe_policy == CS_DUPE_KEEP_LAST) { + return cs_make_newest(cs, hash); + } + return ret ? ret : cs_snip_add(cs, hash, line, nr_lines); } /** diff --git a/src/store.h b/src/store.h index 2eaede3..985e4d4 100644 --- a/src/store.h +++ b/src/store.h @@ -166,7 +166,8 @@ void drop_cs_destroy(struct clip_store *cs); int _must_use_ _nonnull_ cs_content_get(struct clip_store *cs, uint64_t hash, struct cs_content *content); int _must_use_ _nonnull_n_(1) - cs_add(struct clip_store *cs, const char *content, uint64_t *out_hash); + cs_add(struct clip_store *cs, const char *content, uint64_t *out_hash, + enum cs_dupe_policy dupe_policy); bool _must_use_ _nonnull_ cs_snip_iter(struct ref_guard *guard, enum cs_iter_direction direction, struct cs_snip **snip);