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