summaryrefslogtreecommitdiffstats
path: root/paste.c
diff options
context:
space:
mode:
Diffstat (limited to 'paste.c')
-rw-r--r--paste.c326
1 files changed, 326 insertions, 0 deletions
diff --git a/paste.c b/paste.c
new file mode 100644
index 0000000..51ae2b1
--- /dev/null
+++ b/paste.c
@@ -0,0 +1,326 @@
+/* $OpenBSD$ */
+
+/*
+ * Copyright (c) 2007 Nicholas Marriott <nicholas.marriott@gmail.com>
+ *
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF MIND, USE, DATA OR PROFITS, WHETHER
+ * IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING
+ * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#include <sys/types.h>
+
+#include <stdlib.h>
+#include <string.h>
+#include <time.h>
+
+#include "tmux.h"
+
+/*
+ * Set of paste buffers. Note that paste buffer data is not necessarily a C
+ * string!
+ */
+
+struct paste_buffer {
+ char *data;
+ size_t size;
+
+ char *name;
+ time_t created;
+ int automatic;
+ u_int order;
+
+ RB_ENTRY(paste_buffer) name_entry;
+ RB_ENTRY(paste_buffer) time_entry;
+};
+
+static u_int paste_next_index;
+static u_int paste_next_order;
+static u_int paste_num_automatic;
+static RB_HEAD(paste_name_tree, paste_buffer) paste_by_name;
+static RB_HEAD(paste_time_tree, paste_buffer) paste_by_time;
+
+static int paste_cmp_names(const struct paste_buffer *,
+ const struct paste_buffer *);
+RB_GENERATE_STATIC(paste_name_tree, paste_buffer, name_entry, paste_cmp_names);
+
+static int paste_cmp_times(const struct paste_buffer *,
+ const struct paste_buffer *);
+RB_GENERATE_STATIC(paste_time_tree, paste_buffer, time_entry, paste_cmp_times);
+
+static int
+paste_cmp_names(const struct paste_buffer *a, const struct paste_buffer *b)
+{
+ return (strcmp(a->name, b->name));
+}
+
+static int
+paste_cmp_times(const struct paste_buffer *a, const struct paste_buffer *b)
+{
+ if (a->order > b->order)
+ return (-1);
+ if (a->order < b->order)
+ return (1);
+ return (0);
+}
+
+/* Get paste buffer name. */
+const char *
+paste_buffer_name(struct paste_buffer *pb)
+{
+ return (pb->name);
+}
+
+/* Get paste buffer order. */
+u_int
+paste_buffer_order(struct paste_buffer *pb)
+{
+ return (pb->order);
+}
+
+/* Get paste buffer created. */
+time_t
+paste_buffer_created(struct paste_buffer *pb)
+{
+ return (pb->created);
+}
+
+/* Get paste buffer data. */
+const char *
+paste_buffer_data(struct paste_buffer *pb, size_t *size)
+{
+ if (size != NULL)
+ *size = pb->size;
+ return (pb->data);
+}
+
+/* Walk paste buffers by time. */
+struct paste_buffer *
+paste_walk(struct paste_buffer *pb)
+{
+ if (pb == NULL)
+ return (RB_MIN(paste_time_tree, &paste_by_time));
+ return (RB_NEXT(paste_time_tree, &paste_by_time, pb));
+}
+
+/* Get the most recent automatic buffer. */
+struct paste_buffer *
+paste_get_top(const char **name)
+{
+ struct paste_buffer *pb;
+
+ pb = RB_MIN(paste_time_tree, &paste_by_time);
+ if (pb == NULL)
+ return (NULL);
+ if (name != NULL)
+ *name = pb->name;
+ return (pb);
+}
+
+/* Get a paste buffer by name. */
+struct paste_buffer *
+paste_get_name(const char *name)
+{
+ struct paste_buffer pbfind;
+
+ if (name == NULL || *name == '\0')
+ return (NULL);
+
+ pbfind.name = (char *)name;
+ return (RB_FIND(paste_name_tree, &paste_by_name, &pbfind));
+}
+
+/* Free a paste buffer. */
+void
+paste_free(struct paste_buffer *pb)
+{
+ RB_REMOVE(paste_name_tree, &paste_by_name, pb);
+ RB_REMOVE(paste_time_tree, &paste_by_time, pb);
+ if (pb->automatic)
+ paste_num_automatic--;
+
+ free(pb->data);
+ free(pb->name);
+ free(pb);
+}
+
+/*
+ * Add an automatic buffer, freeing the oldest automatic item if at limit. Note
+ * that the caller is responsible for allocating data.
+ */
+void
+paste_add(const char *prefix, char *data, size_t size)
+{
+ struct paste_buffer *pb, *pb1;
+ u_int limit;
+
+ if (prefix == NULL)
+ prefix = "buffer";
+
+ if (size == 0) {
+ free(data);
+ return;
+ }
+
+ limit = options_get_number(global_options, "buffer-limit");
+ RB_FOREACH_REVERSE_SAFE(pb, paste_time_tree, &paste_by_time, pb1) {
+ if (paste_num_automatic < limit)
+ break;
+ if (pb->automatic)
+ paste_free(pb);
+ }
+
+ pb = xmalloc(sizeof *pb);
+
+ pb->name = NULL;
+ do {
+ free(pb->name);
+ xasprintf(&pb->name, "%s%u", prefix, paste_next_index);
+ paste_next_index++;
+ } while (paste_get_name(pb->name) != NULL);
+
+ pb->data = data;
+ pb->size = size;
+
+ pb->automatic = 1;
+ paste_num_automatic++;
+
+ pb->created = time(NULL);
+
+ pb->order = paste_next_order++;
+ RB_INSERT(paste_name_tree, &paste_by_name, pb);
+ RB_INSERT(paste_time_tree, &paste_by_time, pb);
+}
+
+/* Rename a paste buffer. */
+int
+paste_rename(const char *oldname, const char *newname, char **cause)
+{
+ struct paste_buffer *pb, *pb_new;
+
+ if (cause != NULL)
+ *cause = NULL;
+
+ if (oldname == NULL || *oldname == '\0') {
+ if (cause != NULL)
+ *cause = xstrdup("no buffer");
+ return (-1);
+ }
+ if (newname == NULL || *newname == '\0') {
+ if (cause != NULL)
+ *cause = xstrdup("new name is empty");
+ return (-1);
+ }
+
+ pb = paste_get_name(oldname);
+ if (pb == NULL) {
+ if (cause != NULL)
+ xasprintf(cause, "no buffer %s", oldname);
+ return (-1);
+ }
+
+ pb_new = paste_get_name(newname);
+ if (pb_new != NULL) {
+ if (cause != NULL)
+ xasprintf(cause, "buffer %s already exists", newname);
+ return (-1);
+ }
+
+ RB_REMOVE(paste_name_tree, &paste_by_name, pb);
+
+ free(pb->name);
+ pb->name = xstrdup(newname);
+
+ if (pb->automatic)
+ paste_num_automatic--;
+ pb->automatic = 0;
+
+ RB_INSERT(paste_name_tree, &paste_by_name, pb);
+
+ return (0);
+}
+
+/*
+ * Add or replace an item in the store. Note that the caller is responsible for
+ * allocating data.
+ */
+int
+paste_set(char *data, size_t size, const char *name, char **cause)
+{
+ struct paste_buffer *pb, *old;
+
+ if (cause != NULL)
+ *cause = NULL;
+
+ if (size == 0) {
+ free(data);
+ return (0);
+ }
+ if (name == NULL) {
+ paste_add(NULL, data, size);
+ return (0);
+ }
+
+ if (*name == '\0') {
+ if (cause != NULL)
+ *cause = xstrdup("empty buffer name");
+ return (-1);
+ }
+
+ pb = xmalloc(sizeof *pb);
+
+ pb->name = xstrdup(name);
+
+ pb->data = data;
+ pb->size = size;
+
+ pb->automatic = 0;
+ pb->order = paste_next_order++;
+
+ pb->created = time(NULL);
+
+ if ((old = paste_get_name(name)) != NULL)
+ paste_free(old);
+
+ RB_INSERT(paste_name_tree, &paste_by_name, pb);
+ RB_INSERT(paste_time_tree, &paste_by_time, pb);
+
+ return (0);
+}
+
+/* Set paste data without otherwise changing it. */
+void
+paste_replace(struct paste_buffer *pb, char *data, size_t size)
+{
+ free(pb->data);
+ pb->data = data;
+ pb->size = size;
+}
+
+/* Convert start of buffer into a nice string. */
+char *
+paste_make_sample(struct paste_buffer *pb)
+{
+ char *buf;
+ size_t len, used;
+ const int flags = VIS_OCTAL|VIS_CSTYLE|VIS_TAB|VIS_NL;
+ const size_t width = 200;
+
+ len = pb->size;
+ if (len > width)
+ len = width;
+ buf = xreallocarray(NULL, len, 4 + 4);
+
+ used = utf8_strvis(buf, pb->data, len, flags);
+ if (pb->size > width || used > width)
+ strlcpy(buf + width, "...", 4);
+ return (buf);
+}