aboutsummaryrefslogtreecommitdiff
path: root/paste.c
diff options
context:
space:
mode:
Diffstat (limited to 'paste.c')
-rw-r--r--paste.c245
1 files changed, 184 insertions, 61 deletions
diff --git a/paste.c b/paste.c
index bc9b4686..998b975a 100644
--- a/paste.c
+++ b/paste.c
@@ -1,4 +1,4 @@
-/* $Id$ */
+/* $OpenBSD$ */
/*
* Copyright (c) 2007 Nicholas Marriott <nicm@users.sourceforge.net>
@@ -25,159 +25,282 @@
#include "tmux.h"
/*
- * Stack of paste buffers. Note that paste buffer data is not necessarily a C
+ * Set of paste buffers. Note that paste buffer data is not necessarily a C
* string!
*/
-/* Return each item of the stack in turn. */
-struct paste_buffer *
-paste_walk_stack(struct paste_stack *ps, u_int *idx)
+u_int paste_next_index;
+u_int paste_next_order;
+u_int paste_num_automatic;
+RB_HEAD(paste_name_tree, paste_buffer) paste_by_name;
+RB_HEAD(paste_time_tree, paste_buffer) paste_by_time;
+
+int paste_cmp_names(const struct paste_buffer *, const struct paste_buffer *);
+RB_PROTOTYPE(paste_name_tree, paste_buffer, name_entry, paste_cmp_names);
+RB_GENERATE(paste_name_tree, paste_buffer, name_entry, paste_cmp_names);
+
+int paste_cmp_times(const struct paste_buffer *, const struct paste_buffer *);
+RB_PROTOTYPE(paste_time_tree, paste_buffer, time_entry, paste_cmp_times);
+RB_GENERATE(paste_time_tree, paste_buffer, time_entry, paste_cmp_times);
+
+int
+paste_cmp_names(const struct paste_buffer *a, const struct paste_buffer *b)
{
- struct paste_buffer *pb;
+ return (strcmp(a->name, b->name));
+}
- pb = paste_get_index(ps, *idx);
- (*idx)++;
- return (pb);
+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 the top item on the stack. */
+/* Walk paste buffers by name. */
struct paste_buffer *
-paste_get_top(struct paste_stack *ps)
+paste_walk(struct paste_buffer *pb)
{
- if (ARRAY_LENGTH(ps) == 0)
- return (NULL);
- return (ARRAY_FIRST(ps));
+ if (pb == NULL)
+ return (RB_MIN(paste_time_tree, &paste_by_time));
+ return (RB_NEXT(paste_time_tree, &paste_by_time, pb));
}
-/* Get an item by its index. */
+/* Get the most recent automatic buffer. */
struct paste_buffer *
-paste_get_index(struct paste_stack *ps, u_int idx)
+paste_get_top(void)
{
- if (idx >= ARRAY_LENGTH(ps))
+ struct paste_buffer *pb;
+
+ pb = RB_MIN(paste_time_tree, &paste_by_time);
+ if (pb == NULL)
return (NULL);
- return (ARRAY_ITEM(ps, idx));
+ return (pb);
}
-/* Free the top item on the stack. */
+/* Free the most recent buffer. */
int
-paste_free_top(struct paste_stack *ps)
+paste_free_top(void)
{
struct paste_buffer *pb;
- if (ARRAY_LENGTH(ps) == 0)
+ pb = paste_get_top();
+ if (pb == NULL)
return (-1);
+ return (paste_free_name(pb->name));
+}
- pb = ARRAY_FIRST(ps);
- ARRAY_REMOVE(ps, 0);
+/* Get a paste buffer by name. */
+struct paste_buffer *
+paste_get_name(const char *name)
+{
+ struct paste_buffer pbfind;
- free(pb->data);
- free(pb);
+ if (name == NULL || *name == '\0')
+ return (NULL);
- return (0);
+ pbfind.name = (char *)name;
+ return (RB_FIND(paste_name_tree, &paste_by_name, &pbfind));
}
-/* Free an item by index. */
+/* Free a paste buffer by name. */
int
-paste_free_index(struct paste_stack *ps, u_int idx)
+paste_free_name(const char *name)
{
- struct paste_buffer *pb;
+ struct paste_buffer *pb, pbfind;
+
+ if (name == NULL || *name == '\0')
+ return (-1);
- if (idx >= ARRAY_LENGTH(ps))
+ pbfind.name = (char *)name;
+ pb = RB_FIND(paste_name_tree, &paste_by_name, &pbfind);
+ if (pb == NULL)
return (-1);
- pb = ARRAY_ITEM(ps, idx);
- ARRAY_REMOVE(ps, idx);
+ 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);
-
return (0);
}
/*
- * Add an item onto the top of the stack, freeing the bottom if at limit. Note
+ * Add an automatic buffer, freeing the oldest automatic item if at limit. Note
* that the caller is responsible for allocating data.
*/
void
-paste_add(struct paste_stack *ps, char *data, size_t size, u_int limit)
+paste_add(char *data, size_t size)
{
- struct paste_buffer *pb;
+ struct paste_buffer *pb, *pb1;
+ u_int limit;
if (size == 0)
return;
- while (ARRAY_LENGTH(ps) >= limit) {
- pb = ARRAY_LAST(ps);
- free(pb->data);
- free(pb);
- ARRAY_TRUNC(ps, 1);
+ 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_name(pb->name);
}
pb = xmalloc(sizeof *pb);
- ARRAY_INSERT(ps, 0, pb);
+
+ pb->name = NULL;
+ do {
+ free(pb->name);
+ xasprintf(&pb->name, "buffer%04u", 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->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);
+}
/*
- * Replace an item on the stack. Note that the caller is responsible for
+ * Add or replace an item in the store. Note that the caller is responsible for
* allocating data.
*/
int
-paste_replace(struct paste_stack *ps, u_int idx, char *data, size_t size)
+paste_set(char *data, size_t size, const char *name, char **cause)
{
struct paste_buffer *pb;
+ if (cause != NULL)
+ *cause = NULL;
+
if (size == 0) {
free(data);
return (0);
}
+ if (name == NULL) {
+ paste_add(data, size);
+ return (0);
+ }
- if (idx >= ARRAY_LENGTH(ps))
+ if (*name == '\0') {
+ if (cause != NULL)
+ *cause = xstrdup("empty buffer name");
return (-1);
+ }
- pb = ARRAY_ITEM(ps, idx);
- free(pb->data);
+ pb = paste_get_name(name);
+ if (pb != NULL)
+ paste_free_name(name);
+
+ pb = xmalloc(sizeof *pb);
+
+ pb->name = xstrdup(name);
pb->data = data;
pb->size = size;
+ pb->automatic = 0;
+ pb->order = paste_next_order++;
+
+ RB_INSERT(paste_name_tree, &paste_by_name, pb);
+ RB_INSERT(paste_time_tree, &paste_by_time, pb);
+
return (0);
}
-/* Convert a buffer into a visible string. */
+/* Convert start of buffer into a nice string. */
char *
-paste_print(struct paste_buffer *pb, size_t width)
+paste_make_sample(struct paste_buffer *pb, int utf8flag)
{
- char *buf;
- size_t len, used;
-
- if (width < 3)
- width = 3;
- buf = xmalloc(width * 4 + 1);
+ char *buf;
+ size_t len, used;
+ const int flags = VIS_OCTAL|VIS_TAB|VIS_NL;
+ const size_t width = 200;
len = pb->size;
if (len > width)
len = width;
+ buf = xreallocarray(NULL, len, 4 + 4);
- used = strvisx(buf, pb->data, len, VIS_OCTAL|VIS_TAB|VIS_NL);
+ if (utf8flag)
+ used = utf8_strvis(buf, pb->data, len, flags);
+ else
+ used = strvisx(buf, pb->data, len, flags);
if (pb->size > width || used > width)
- strlcpy(buf + width - 3, "...", 4);
-
+ strlcpy(buf + width, "...", 4);
return (buf);
}
/* Paste into a window pane, filtering '\n' according to separator. */
void
-paste_send_pane (struct paste_buffer *pb, struct window_pane *wp,
+paste_send_pane(struct paste_buffer *pb, struct window_pane *wp,
const char *sep, int bracket)
{
const char *data = pb->data, *end = data + pb->size, *lf;
size_t seplen;
- if (bracket)
+ if (wp->flags & PANE_INPUTOFF)
+ return;
+
+ if (bracket && (wp->screen->mode & MODE_BRACKETPASTE))
bufferevent_write(wp->event, "\033[200~", 6);
seplen = strlen(sep);
@@ -191,6 +314,6 @@ paste_send_pane (struct paste_buffer *pb, struct window_pane *wp,
if (end != data)
bufferevent_write(wp->event, data, end - data);
- if (bracket)
+ if (bracket && (wp->screen->mode & MODE_BRACKETPASTE))
bufferevent_write(wp->event, "\033[201~", 6);
}