xref: /openbsd-src/usr.bin/tmux/paste.c (revision ca6784669ed926e64c92bbcc7839f5934b2c6817)
1 /* $OpenBSD: paste.c,v 1.18 2014/04/24 09:14:43 nicm Exp $ */
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 
22 #include <stdlib.h>
23 #include <string.h>
24 #include <vis.h>
25 
26 #include "tmux.h"
27 
28 /*
29  * Stack of paste buffers. Note that paste buffer data is not necessarily a C
30  * string!
31  */
32 
33 ARRAY_DECL(, struct paste_buffer *) paste_buffers =  ARRAY_INITIALIZER;
34 
35 /* Return each item of the stack in turn. */
36 struct paste_buffer *
37 paste_walk_stack(u_int *idx)
38 {
39 	struct paste_buffer	*pb;
40 
41 	pb = paste_get_index(*idx);
42 	(*idx)++;
43 	return (pb);
44 }
45 
46 /* Get the top item on the stack. */
47 struct paste_buffer *
48 paste_get_top(void)
49 {
50 	if (ARRAY_LENGTH(&paste_buffers) == 0)
51 		return (NULL);
52 	return (ARRAY_FIRST(&paste_buffers));
53 }
54 
55 /* Get an item by its index. */
56 struct paste_buffer *
57 paste_get_index(u_int idx)
58 {
59 	if (idx >= ARRAY_LENGTH(&paste_buffers))
60 		return (NULL);
61 	return (ARRAY_ITEM(&paste_buffers, idx));
62 }
63 
64 /* Free the top item on the stack. */
65 int
66 paste_free_top(void)
67 {
68 	struct paste_buffer	*pb;
69 
70 	if (ARRAY_LENGTH(&paste_buffers) == 0)
71 		return (-1);
72 
73 	pb = ARRAY_FIRST(&paste_buffers);
74 	ARRAY_REMOVE(&paste_buffers, 0);
75 
76 	free(pb->data);
77 	free(pb);
78 
79 	return (0);
80 }
81 
82 /* Free an item by index. */
83 int
84 paste_free_index(u_int idx)
85 {
86 	struct paste_buffer	*pb;
87 
88 	if (idx >= ARRAY_LENGTH(&paste_buffers))
89 		return (-1);
90 
91 	pb = ARRAY_ITEM(&paste_buffers, idx);
92 	ARRAY_REMOVE(&paste_buffers, idx);
93 
94 	free(pb->data);
95 	free(pb);
96 
97 	return (0);
98 }
99 
100 /*
101  * Add an item onto the top of the stack, freeing the bottom if at limit. Note
102  * that the caller is responsible for allocating data.
103  */
104 void
105 paste_add(char *data, size_t size, u_int limit)
106 {
107 	struct paste_buffer	*pb;
108 
109 	if (size == 0)
110 		return;
111 
112 	while (ARRAY_LENGTH(&paste_buffers) >= limit) {
113 		pb = ARRAY_LAST(&paste_buffers);
114 		free(pb->data);
115 		free(pb);
116 		ARRAY_TRUNC(&paste_buffers, 1);
117 	}
118 
119 	pb = xmalloc(sizeof *pb);
120 	ARRAY_INSERT(&paste_buffers, 0, pb);
121 
122 	pb->data = data;
123 	pb->size = size;
124 }
125 
126 
127 /*
128  * Replace an item on the stack. Note that the caller is responsible for
129  * allocating data.
130  */
131 int
132 paste_replace(u_int idx, char *data, size_t size)
133 {
134 	struct paste_buffer	*pb;
135 
136 	if (size == 0) {
137 		free(data);
138 		return (0);
139 	}
140 
141 	if (idx >= ARRAY_LENGTH(&paste_buffers))
142 		return (-1);
143 
144 	pb = ARRAY_ITEM(&paste_buffers, idx);
145 	free(pb->data);
146 
147 	pb->data = data;
148 	pb->size = size;
149 
150 	return (0);
151 }
152 
153 /* Convert start of buffer into a nice string. */
154 char *
155 paste_make_sample(struct paste_buffer *pb, int utf8flag)
156 {
157 	char		*buf;
158 	size_t		 len, used;
159 	const int	 flags = VIS_OCTAL|VIS_TAB|VIS_NL;
160 	const size_t	 width = 200;
161 
162 	len = pb->size;
163 	if (len > width)
164 		len = width;
165 	buf = xmalloc(len * 4 + 4);
166 
167 	if (utf8flag)
168 		used = utf8_strvis(buf, pb->data, len, flags);
169 	else
170 		used = strvisx(buf, pb->data, len, flags);
171 	if (pb->size > width || used > width)
172 		strlcpy(buf + width, "...", 4);
173 	return (buf);
174 }
175 
176 /* Paste into a window pane, filtering '\n' according to separator. */
177 void
178 paste_send_pane(struct paste_buffer *pb, struct window_pane *wp,
179     const char *sep, int bracket)
180 {
181 	const char	*data = pb->data, *end = data + pb->size, *lf;
182 	size_t		 seplen;
183 
184 	if (bracket)
185 		bufferevent_write(wp->event, "\033[200~", 6);
186 
187 	seplen = strlen(sep);
188 	while ((lf = memchr(data, '\n', end - data)) != NULL) {
189 		if (lf != data)
190 			bufferevent_write(wp->event, data, lf - data);
191 		bufferevent_write(wp->event, sep, seplen);
192 		data = lf + 1;
193 	}
194 
195 	if (end != data)
196 		bufferevent_write(wp->event, data, end - data);
197 
198 	if (bracket)
199 		bufferevent_write(wp->event, "\033[201~", 6);
200 }
201