GCC Code Coverage Report
Directory: ./ Exec Total Coverage
File: usr.bin/tmux/paste.c Lines: 0 114 0.0 %
Date: 2016-12-06 Branches: 0 480 0.0 %

Line Branch Exec Source
1
/* $OpenBSD: paste.c,v 1.34 2016/01/19 15:59:12 nicm Exp $ */
2
3
/*
4
 * Copyright (c) 2007 Nicholas Marriott <nicholas.marriott@gmail.com>
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
 * Set of paste buffers. Note that paste buffer data is not necessarily a C
30
 * string!
31
 */
32
33
struct paste_buffer {
34
	char		*data;
35
	size_t		 size;
36
37
	char		*name;
38
	int		 automatic;
39
	u_int		 order;
40
41
	RB_ENTRY(paste_buffer) name_entry;
42
	RB_ENTRY(paste_buffer) time_entry;
43
};
44
45
u_int	paste_next_index;
46
u_int	paste_next_order;
47
u_int	paste_num_automatic;
48
RB_HEAD(paste_name_tree, paste_buffer) paste_by_name;
49
RB_HEAD(paste_time_tree, paste_buffer) paste_by_time;
50
51
int paste_cmp_names(const struct paste_buffer *, const struct paste_buffer *);
52
RB_PROTOTYPE(paste_name_tree, paste_buffer, name_entry, paste_cmp_names);
53
RB_GENERATE(paste_name_tree, paste_buffer, name_entry, paste_cmp_names);
54
55
int paste_cmp_times(const struct paste_buffer *, const struct paste_buffer *);
56
RB_PROTOTYPE(paste_time_tree, paste_buffer, time_entry, paste_cmp_times);
57
RB_GENERATE(paste_time_tree, paste_buffer, time_entry, paste_cmp_times);
58
59
int
60
paste_cmp_names(const struct paste_buffer *a, const struct paste_buffer *b)
61
{
62
	return (strcmp(a->name, b->name));
63
}
64
65
int
66
paste_cmp_times(const struct paste_buffer *a, const struct paste_buffer *b)
67
{
68
	if (a->order > b->order)
69
		return (-1);
70
	if (a->order < b->order)
71
		return (1);
72
	return (0);
73
}
74
75
/* Get paste buffer name. */
76
const char *
77
paste_buffer_name(struct paste_buffer *pb)
78
{
79
	return (pb->name);
80
}
81
82
/* Get paste buffer data. */
83
const char *
84
paste_buffer_data(struct paste_buffer *pb, size_t *size)
85
{
86
	if (size != NULL)
87
		*size = pb->size;
88
	return (pb->data);
89
}
90
91
/* Walk paste buffers by name. */
92
struct paste_buffer *
93
paste_walk(struct paste_buffer *pb)
94
{
95
	if (pb == NULL)
96
		return (RB_MIN(paste_time_tree, &paste_by_time));
97
	return (RB_NEXT(paste_time_tree, &paste_by_time, pb));
98
}
99
100
/* Get the most recent automatic buffer. */
101
struct paste_buffer *
102
paste_get_top(const char **name)
103
{
104
	struct paste_buffer	*pb;
105
106
	pb = RB_MIN(paste_time_tree, &paste_by_time);
107
	if (pb == NULL)
108
		return (NULL);
109
	if (name != NULL)
110
		*name = pb->name;
111
	return (pb);
112
}
113
114
/* Get a paste buffer by name. */
115
struct paste_buffer *
116
paste_get_name(const char *name)
117
{
118
	struct paste_buffer	pbfind;
119
120
	if (name == NULL || *name == '\0')
121
		return (NULL);
122
123
	pbfind.name = (char *)name;
124
	return (RB_FIND(paste_name_tree, &paste_by_name, &pbfind));
125
}
126
127
/* Free a paste buffer. */
128
void
129
paste_free(struct paste_buffer *pb)
130
{
131
	RB_REMOVE(paste_name_tree, &paste_by_name, pb);
132
	RB_REMOVE(paste_time_tree, &paste_by_time, pb);
133
	if (pb->automatic)
134
		paste_num_automatic--;
135
136
	free(pb->data);
137
	free(pb->name);
138
	free(pb);
139
}
140
141
/*
142
 * Add an automatic buffer, freeing the oldest automatic item if at limit. Note
143
 * that the caller is responsible for allocating data.
144
 */
145
void
146
paste_add(char *data, size_t size)
147
{
148
	struct paste_buffer	*pb, *pb1;
149
	u_int			 limit;
150
151
	if (size == 0)
152
		return;
153
154
	limit = options_get_number(global_options, "buffer-limit");
155
	RB_FOREACH_REVERSE_SAFE(pb, paste_time_tree, &paste_by_time, pb1) {
156
		if (paste_num_automatic < limit)
157
			break;
158
		if (pb->automatic)
159
			paste_free(pb);
160
	}
161
162
	pb = xmalloc(sizeof *pb);
163
164
	pb->name = NULL;
165
	do {
166
		free(pb->name);
167
		xasprintf(&pb->name, "buffer%04u", paste_next_index);
168
		paste_next_index++;
169
	} while (paste_get_name(pb->name) != NULL);
170
171
	pb->data = data;
172
	pb->size = size;
173
174
	pb->automatic = 1;
175
	paste_num_automatic++;
176
177
	pb->order = paste_next_order++;
178
	RB_INSERT(paste_name_tree, &paste_by_name, pb);
179
	RB_INSERT(paste_time_tree, &paste_by_time, pb);
180
}
181
182
/* Rename a paste buffer. */
183
int
184
paste_rename(const char *oldname, const char *newname, char **cause)
185
{
186
	struct paste_buffer	*pb, *pb_new;
187
188
	if (cause != NULL)
189
		*cause = NULL;
190
191
	if (oldname == NULL || *oldname == '\0') {
192
		if (cause != NULL)
193
			*cause = xstrdup("no buffer");
194
		return (-1);
195
	}
196
	if (newname == NULL || *newname == '\0') {
197
		if (cause != NULL)
198
			*cause = xstrdup("new name is empty");
199
		return (-1);
200
	}
201
202
	pb = paste_get_name(oldname);
203
	if (pb == NULL) {
204
		if (cause != NULL)
205
			xasprintf(cause, "no buffer %s", oldname);
206
		return (-1);
207
	}
208
209
	pb_new = paste_get_name(newname);
210
	if (pb_new != NULL) {
211
		if (cause != NULL)
212
			xasprintf(cause, "buffer %s already exists", newname);
213
		return (-1);
214
	}
215
216
	RB_REMOVE(paste_name_tree, &paste_by_name, pb);
217
218
	free(pb->name);
219
	pb->name = xstrdup(newname);
220
221
	if (pb->automatic)
222
		paste_num_automatic--;
223
	pb->automatic = 0;
224
225
	RB_INSERT(paste_name_tree, &paste_by_name, pb);
226
227
	return (0);
228
}
229
230
/*
231
 * Add or replace an item in the store. Note that the caller is responsible for
232
 * allocating data.
233
 */
234
int
235
paste_set(char *data, size_t size, const char *name, char **cause)
236
{
237
	struct paste_buffer	*pb, *old;
238
239
	if (cause != NULL)
240
		*cause = NULL;
241
242
	if (size == 0) {
243
		free(data);
244
		return (0);
245
	}
246
	if (name == NULL) {
247
		paste_add(data, size);
248
		return (0);
249
	}
250
251
	if (*name == '\0') {
252
		if (cause != NULL)
253
			*cause = xstrdup("empty buffer name");
254
		return (-1);
255
	}
256
257
	pb = xmalloc(sizeof *pb);
258
259
	pb->name = xstrdup(name);
260
261
	pb->data = data;
262
	pb->size = size;
263
264
	pb->automatic = 0;
265
	pb->order = paste_next_order++;
266
267
	if ((old = paste_get_name(name)) != NULL)
268
		paste_free(old);
269
270
	RB_INSERT(paste_name_tree, &paste_by_name, pb);
271
	RB_INSERT(paste_time_tree, &paste_by_time, pb);
272
273
	return (0);
274
}
275
276
/* Convert start of buffer into a nice string. */
277
char *
278
paste_make_sample(struct paste_buffer *pb)
279
{
280
	char		*buf;
281
	size_t		 len, used;
282
	const int	 flags = VIS_OCTAL|VIS_TAB|VIS_NL;
283
	const size_t	 width = 200;
284
285
	len = pb->size;
286
	if (len > width)
287
		len = width;
288
	buf = xreallocarray(NULL, len, 4 + 4);
289
290
	used = utf8_strvis(buf, pb->data, len, flags);
291
	if (pb->size > width || used > width)
292
		strlcpy(buf + width, "...", 4);
293
	return (buf);
294
}