From de720d4eac7a2e4e65363e2b9b7601b0f1247bda Mon Sep 17 00:00:00 2001 From: NRK Date: Tue, 18 Jun 2024 16:48:00 +0000 Subject: [PATCH 1/2] store: introduce dupe_policy --- src/store.c | 14 ++++++++------ src/store.h | 11 +++++++++++ 2 files changed, 19 insertions(+), 6 deletions(-) diff --git a/src/store.c b/src/store.c index 17255b5..833bf25 100644 --- a/src/store.c +++ b/src/store.c @@ -401,10 +401,12 @@ static int _must_use_ _nonnull_ cs_snip_add(struct clip_store *cs, * @cs: The clip store to operate on * @hash: The hash of the content to add * @content: The content to add to the file + * @dupe_policy: If set to CS_DUPE_KEEP_LAST, will return with -EEXIST when + * trying to insert duplicate entry. */ -static int _must_use_ _nonnull_ cs_content_add(struct clip_store *cs, - uint64_t hash, - const char *content) { +static int _must_use_ _nonnull_ +cs_content_add(struct clip_store *cs, uint64_t hash, const char *content, + enum cs_dupe_policy dupe_policy) { bool dupe = false; char dir_path[CS_HASH_STR_MAX]; @@ -412,7 +414,7 @@ static int _must_use_ _nonnull_ cs_content_add(struct clip_store *cs, int ret = mkdirat(cs->content_dir_fd, dir_path, 0700); if (ret < 0) { - if (errno != EEXIST) { + if (errno != EEXIST || dupe_policy == CS_DUPE_KEEP_LAST) { return negative_errno(); } dupe = true; @@ -537,7 +539,7 @@ int cs_add(struct clip_store *cs, const char *content, uint64_t *out_hash) { char line[CS_SNIP_LINE_SIZE]; size_t nr_lines = first_line(content, line); - int ret = cs_content_add(cs, hash, content); + int ret = cs_content_add(cs, hash, content, CS_DUPE_KEEP_ALL); if (ret < 0) { return ret; } @@ -767,7 +769,7 @@ int cs_replace(struct clip_store *cs, enum cs_iter_direction direction, size_t nr_lines = first_line(content, line); uint64_t hash = djb64_hash(content); cs_snip_update(snip, hash, line, nr_lines); - ret = cs_content_add(cs, hash, content); + ret = cs_content_add(cs, hash, content, CS_DUPE_KEEP_ALL); if (ret) { return ret; } diff --git a/src/store.h b/src/store.h index 650cc8b..71b1bfe 100644 --- a/src/store.h +++ b/src/store.h @@ -144,6 +144,17 @@ enum cs_remove_action { CS_ACTION_STOP = BIT(2), }; +/** + * What to do when there's a duplicate entry. + * + * @CS_DUPE_KEEP_ALL: Keep all duplicate entries. + * @CS_DUPE_KEEP_LAST: Only keep the newest, do not insert duplicate entries. + */ +enum cs_dupe_policy { + CS_DUPE_KEEP_ALL, + CS_DUPE_KEEP_LAST, +}; + struct ref_guard _must_use_ _nonnull_ cs_ref(struct clip_store *cs); void _nonnull_ cs_unref(struct clip_store *cs); void _nonnull_ drop_cs_unref(struct ref_guard *guard); From ca4fe427aac785a1e3310e4be98a166eadc44744 Mon Sep 17 00:00:00 2001 From: NRK Date: Tue, 18 Jun 2024 17:22:15 +0000 Subject: [PATCH 2/2] config: Add an option to deduplicate entries Closes: https://github.com/cdown/clipmenu/issues/224 --- src/clipmenud.c | 4 +++- src/config.c | 2 ++ src/config.h | 1 + src/store.c | 39 ++++++++++++++++++++++++++++++++------- src/store.h | 3 ++- 5 files changed, 40 insertions(+), 9 deletions(-) diff --git a/src/clipmenud.c b/src/clipmenud.c index 08e0785..f88f07e 100644 --- a/src/clipmenud.c +++ b/src/clipmenud.c @@ -269,7 +269,9 @@ static uint64_t store_clip(struct clip_text *ct) { dbg("Possible partial of last clip, replacing\n"); expect(cs_replace(&cs, CS_ITER_NEWEST_FIRST, 0, ct->data, &hash) == 0); } else { - expect(cs_add(&cs, ct->data, &hash) == 0); + expect(cs_add(&cs, ct->data, &hash, + cfg.deduplicate ? CS_DUPE_KEEP_LAST : CS_DUPE_KEEP_ALL) == + 0); } free_clip_text(&last_text); diff --git a/src/config.c b/src/config.c index 4a31791..84366fa 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 bfec09b..e7cb7c5 100644 --- a/src/config.h +++ b/src/config.h @@ -44,6 +44,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 833bf25..e2e7a69 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 71b1bfe..3acbf39 100644 --- a/src/store.h +++ b/src/store.h @@ -167,7 +167,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);