Annotation of src/usr.bin/tmux/paste.c, Revision 1.21
1.21 ! nicm 1: /* $OpenBSD: paste.c,v 1.20 2014/05/13 22:54:18 nicm Exp $ */
1.1 nicm 2:
3: /*
4: * Copyright (c) 2007 Nicholas Marriott <nicm@users.sourceforge.net>
5: *
6: * Permission to use, copy, modify, and distribute this software for any
7: * purpose with or without fee is hereby granted, provided that the above
8: * copyright notice and this permission notice appear in all copies.
9: *
10: * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
11: * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
12: * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
13: * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
14: * WHATSOEVER RESULTING FROM LOSS OF MIND, USE, DATA OR PROFITS, WHETHER
15: * IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING
16: * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
17: */
18:
19: #include <sys/types.h>
20: #include <sys/time.h>
21:
1.12 nicm 22: #include <stdlib.h>
1.1 nicm 23: #include <string.h>
1.9 nicm 24: #include <vis.h>
1.1 nicm 25:
26: #include "tmux.h"
27:
1.7 nicm 28: /*
1.19 nicm 29: * Set of paste buffers. Note that paste buffer data is not necessarily a C
1.7 nicm 30: * string!
31: */
1.1 nicm 32:
1.19 nicm 33: u_int paste_next_index;
34: u_int paste_next_order;
35: u_int paste_num_automatic;
36: RB_HEAD(paste_name_tree, paste_buffer) paste_by_name;
37: RB_HEAD(paste_time_tree, paste_buffer) paste_by_time;
38:
39: int paste_cmp_names(const struct paste_buffer *, const struct paste_buffer *);
40: RB_PROTOTYPE(paste_name_tree, paste_buffer, name_entry, paste_cmp_names);
41: RB_GENERATE(paste_name_tree, paste_buffer, name_entry, paste_cmp_names);
42:
43: int paste_cmp_times(const struct paste_buffer *, const struct paste_buffer *);
44: RB_PROTOTYPE(paste_time_tree, paste_buffer, time_entry, paste_cmp_times);
45: RB_GENERATE(paste_time_tree, paste_buffer, time_entry, paste_cmp_times);
1.18 nicm 46:
1.19 nicm 47: int
48: paste_cmp_names(const struct paste_buffer *a, const struct paste_buffer *b)
1.1 nicm 49: {
1.19 nicm 50: return (strcmp(a->name, b->name));
51: }
1.1 nicm 52:
1.19 nicm 53: int
54: paste_cmp_times(const struct paste_buffer *a, const struct paste_buffer *b)
55: {
56: if (a->order > b->order)
57: return (-1);
58: if (a->order < b->order)
59: return (1);
60: return (0);
1.1 nicm 61: }
62:
1.19 nicm 63: /* Walk paste buffers by name. */
1.1 nicm 64: struct paste_buffer *
1.19 nicm 65: paste_walk(struct paste_buffer *pb)
1.1 nicm 66: {
1.19 nicm 67: if (pb == NULL)
68: return (RB_MIN(paste_time_tree, &paste_by_time));
69: return (RB_NEXT(paste_time_tree, &paste_by_time, pb));
1.1 nicm 70: }
71:
1.21 ! nicm 72: /* Get the most recent automatic buffer. */
1.1 nicm 73: struct paste_buffer *
1.19 nicm 74: paste_get_top(void)
1.1 nicm 75: {
1.19 nicm 76: struct paste_buffer *pb;
77:
78: pb = RB_MIN(paste_time_tree, &paste_by_time);
79: if (pb == NULL)
1.1 nicm 80: return (NULL);
1.19 nicm 81: return (pb);
1.1 nicm 82: }
83:
1.21 ! nicm 84: /* Free the most recent buffer. */
1.1 nicm 85: int
1.18 nicm 86: paste_free_top(void)
1.1 nicm 87: {
88: struct paste_buffer *pb;
89:
1.19 nicm 90: pb = paste_get_top();
91: if (pb == NULL)
1.1 nicm 92: return (-1);
1.19 nicm 93: return (paste_free_name(pb->name));
94: }
1.1 nicm 95:
1.19 nicm 96: /* Get a paste buffer by name. */
97: struct paste_buffer *
98: paste_get_name(const char *name)
99: {
100: struct paste_buffer pbfind;
1.1 nicm 101:
1.19 nicm 102: if (name == NULL || *name == '\0')
103: return (NULL);
1.1 nicm 104:
1.19 nicm 105: pbfind.name = (char*)name;
106: return (RB_FIND(paste_name_tree, &paste_by_name, &pbfind));
1.1 nicm 107: }
108:
1.19 nicm 109: /* Free a paste buffer by name. */
1.1 nicm 110: int
1.19 nicm 111: paste_free_name(const char *name)
1.1 nicm 112: {
1.19 nicm 113: struct paste_buffer *pb, pbfind;
114:
115: if (name == NULL || *name == '\0')
116: return (-1);
1.1 nicm 117:
1.19 nicm 118: pbfind.name = (char*)name;
119: pb = RB_FIND(paste_name_tree, &paste_by_name, &pbfind);
120: if (pb == NULL)
1.1 nicm 121: return (-1);
122:
1.19 nicm 123: RB_REMOVE(paste_name_tree, &paste_by_name, pb);
124: RB_REMOVE(paste_time_tree, &paste_by_time, pb);
125: if (pb->automatic)
126: paste_num_automatic--;
1.1 nicm 127:
1.12 nicm 128: free(pb->data);
1.19 nicm 129: free(pb->name);
1.12 nicm 130: free(pb);
1.1 nicm 131: return (0);
132: }
133:
1.8 nicm 134: /*
1.19 nicm 135: * Add an automatic buffer, freeing the oldest automatic item if at limit. Note
1.7 nicm 136: * that the caller is responsible for allocating data.
137: */
1.1 nicm 138: void
1.19 nicm 139: paste_add(char *data, size_t size)
1.1 nicm 140: {
1.19 nicm 141: struct paste_buffer *pb, *pb1;
142: u_int limit;
1.2 nicm 143:
1.7 nicm 144: if (size == 0)
1.2 nicm 145: return;
1.1 nicm 146:
1.19 nicm 147: limit = options_get_number(&global_options, "buffer-limit");
148: RB_FOREACH_REVERSE_SAFE(pb, paste_time_tree, &paste_by_time, pb1) {
149: if (paste_num_automatic < limit)
150: break;
151: if (pb->automatic)
152: paste_free_name(pb->name);
1.3 nicm 153: }
1.1 nicm 154:
155: pb = xmalloc(sizeof *pb);
1.19 nicm 156:
157: pb->name = NULL;
158: do {
159: free(pb->name);
160: xasprintf(&pb->name, "buffer%04u", paste_next_index);
161: paste_next_index++;
162: } while (paste_get_name(pb->name) != NULL);
1.1 nicm 163:
164: pb->data = data;
1.4 nicm 165: pb->size = size;
1.19 nicm 166:
167: pb->automatic = 1;
168: paste_num_automatic++;
169:
170: pb->order = paste_next_order++;
171: RB_INSERT(paste_name_tree, &paste_by_name, pb);
172: RB_INSERT(paste_time_tree, &paste_by_time, pb);
1.1 nicm 173: }
174:
1.19 nicm 175: /* Rename a paste buffer. */
176: int
177: paste_rename(const char *oldname, const char *newname, char **cause)
178: {
1.20 nicm 179: struct paste_buffer *pb, *pb_new;
1.19 nicm 180:
181: if (cause != NULL)
182: *cause = NULL;
183:
184: if (oldname == NULL || *oldname == '\0') {
185: if (cause != NULL)
186: *cause = xstrdup("no buffer");
187: return (-1);
188: }
189: if (newname == NULL || *newname == '\0') {
190: if (cause != NULL)
191: *cause = xstrdup("new name is empty");
192: return (-1);
193: }
194:
195: pb = paste_get_name(oldname);
196: if (pb == NULL) {
197: if (cause != NULL)
1.20 nicm 198: xasprintf(cause, "no buffer %s", oldname);
199: return (-1);
200: }
201:
202: pb_new = paste_get_name(newname);
203: if (pb_new != NULL) {
204: if (cause != NULL)
205: xasprintf(cause, "buffer %s already exists", newname);
1.19 nicm 206: return (-1);
207: }
208:
209: RB_REMOVE(paste_name_tree, &paste_by_name, pb);
210:
211: free(pb->name);
212: pb->name = xstrdup(newname);
213:
214: if (pb->automatic)
215: paste_num_automatic--;
216: pb->automatic = 0;
217:
218: RB_INSERT(paste_name_tree, &paste_by_name, pb);
219:
220: return (0);
221: }
1.7 nicm 222:
1.8 nicm 223: /*
1.19 nicm 224: * Add or replace an item in the store. Note that the caller is responsible for
1.7 nicm 225: * allocating data.
226: */
1.1 nicm 227: int
1.19 nicm 228: paste_set(char *data, size_t size, const char *name, char **cause)
1.1 nicm 229: {
230: struct paste_buffer *pb;
1.7 nicm 231:
1.19 nicm 232: if (cause != NULL)
233: *cause = NULL;
234:
1.15 nicm 235: if (size == 0) {
236: free(data);
1.7 nicm 237: return (0);
1.15 nicm 238: }
1.19 nicm 239: if (name == NULL) {
240: paste_add(data, size);
241: return (0);
242: }
1.1 nicm 243:
1.19 nicm 244: if (*name == '\0') {
245: if (cause != NULL)
246: *cause = xstrdup("empty buffer name");
1.1 nicm 247: return (-1);
1.19 nicm 248: }
249:
250: pb = paste_get_name(name);
251: if (pb != NULL)
252: paste_free_name(name);
253:
254: pb = xmalloc(sizeof *pb);
1.1 nicm 255:
1.19 nicm 256: pb->name = xstrdup(name);
1.1 nicm 257:
258: pb->data = data;
1.4 nicm 259: pb->size = size;
1.19 nicm 260:
261: pb->automatic = 0;
262: pb->order = paste_next_order++;
263:
264: RB_INSERT(paste_name_tree, &paste_by_name, pb);
265: RB_INSERT(paste_time_tree, &paste_by_time, pb);
1.1 nicm 266:
267: return (0);
1.9 nicm 268: }
269:
1.17 nicm 270: /* Convert start of buffer into a nice string. */
1.9 nicm 271: char *
1.17 nicm 272: paste_make_sample(struct paste_buffer *pb, int utf8flag)
1.9 nicm 273: {
1.17 nicm 274: char *buf;
275: size_t len, used;
276: const int flags = VIS_OCTAL|VIS_TAB|VIS_NL;
277: const size_t width = 200;
1.9 nicm 278:
279: len = pb->size;
280: if (len > width)
281: len = width;
1.17 nicm 282: buf = xmalloc(len * 4 + 4);
1.9 nicm 283:
1.17 nicm 284: if (utf8flag)
285: used = utf8_strvis(buf, pb->data, len, flags);
286: else
287: used = strvisx(buf, pb->data, len, flags);
1.13 nicm 288: if (pb->size > width || used > width)
1.17 nicm 289: strlcpy(buf + width, "...", 4);
1.9 nicm 290: return (buf);
1.14 nicm 291: }
292:
293: /* Paste into a window pane, filtering '\n' according to separator. */
294: void
1.16 nicm 295: paste_send_pane(struct paste_buffer *pb, struct window_pane *wp,
1.14 nicm 296: const char *sep, int bracket)
297: {
298: const char *data = pb->data, *end = data + pb->size, *lf;
299: size_t seplen;
300:
301: if (bracket)
302: bufferevent_write(wp->event, "\033[200~", 6);
303:
304: seplen = strlen(sep);
305: while ((lf = memchr(data, '\n', end - data)) != NULL) {
306: if (lf != data)
307: bufferevent_write(wp->event, data, lf - data);
308: bufferevent_write(wp->event, sep, seplen);
309: data = lf + 1;
310: }
311:
312: if (end != data)
313: bufferevent_write(wp->event, data, end - data);
314:
315: if (bracket)
316: bufferevent_write(wp->event, "\033[201~", 6);
1.1 nicm 317: }