Coverage Report

Created: 2025-07-11 06:20

/src/tmux/paste.c
Line
Count
Source (jump to first uncovered line)
1
/* $OpenBSD$ */
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
21
#include <stdlib.h>
22
#include <string.h>
23
#include <time.h>
24
25
#include "tmux.h"
26
27
/*
28
 * Set of paste buffers. Note that paste buffer data is not necessarily a C
29
 * string!
30
 */
31
32
struct paste_buffer {
33
  char    *data;
34
  size_t     size;
35
36
  char    *name;
37
  time_t     created;
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
static u_int  paste_next_index;
46
static u_int  paste_next_order;
47
static u_int  paste_num_automatic;
48
static RB_HEAD(paste_name_tree, paste_buffer) paste_by_name;
49
static RB_HEAD(paste_time_tree, paste_buffer) paste_by_time;
50
51
static int  paste_cmp_names(const struct paste_buffer *,
52
        const struct paste_buffer *);
53
RB_GENERATE_STATIC(paste_name_tree, paste_buffer, name_entry, paste_cmp_names);
54
55
static int  paste_cmp_times(const struct paste_buffer *,
56
        const struct paste_buffer *);
57
RB_GENERATE_STATIC(paste_time_tree, paste_buffer, time_entry, paste_cmp_times);
58
59
static int
60
paste_cmp_names(const struct paste_buffer *a, const struct paste_buffer *b)
61
17.5k
{
62
17.5k
  return (strcmp(a->name, b->name));
63
17.5k
}
64
65
static int
66
paste_cmp_times(const struct paste_buffer *a, const struct paste_buffer *b)
67
8.92k
{
68
8.92k
  if (a->order > b->order)
69
8.92k
    return (-1);
70
0
  if (a->order < b->order)
71
0
    return (1);
72
0
  return (0);
73
0
}
74
75
/* Get paste buffer name. */
76
const char *
77
paste_buffer_name(struct paste_buffer *pb)
78
0
{
79
0
  return (pb->name);
80
0
}
81
82
/* Get paste buffer order. */
83
u_int
84
paste_buffer_order(struct paste_buffer *pb)
85
0
{
86
0
  return (pb->order);
87
0
}
88
89
/* Get paste buffer created. */
90
time_t
91
paste_buffer_created(struct paste_buffer *pb)
92
0
{
93
0
  return (pb->created);
94
0
}
95
96
/* Get paste buffer data. */
97
const char *
98
paste_buffer_data(struct paste_buffer *pb, size_t *size)
99
205
{
100
205
  if (size != NULL)
101
205
    *size = pb->size;
102
205
  return (pb->data);
103
205
}
104
105
/* Walk paste buffers by time. */
106
struct paste_buffer *
107
paste_walk(struct paste_buffer *pb)
108
0
{
109
0
  if (pb == NULL)
110
0
    return (RB_MIN(paste_time_tree, &paste_by_time));
111
0
  return (RB_NEXT(paste_time_tree, &paste_by_time, pb));
112
0
}
113
114
int
115
paste_is_empty(void)
116
0
{
117
0
  return RB_ROOT(&paste_by_time) == NULL;
118
0
}
119
120
/* Get the most recent automatic buffer. */
121
struct paste_buffer *
122
paste_get_top(const char **name)
123
4.06k
{
124
4.06k
  struct paste_buffer *pb;
125
126
4.06k
  pb = RB_MIN(paste_time_tree, &paste_by_time);
127
4.06k
  while (pb != NULL && !pb->automatic)
128
0
    pb = RB_NEXT(paste_time_tree, &paste_by_time, pb);
129
4.06k
  if (pb == NULL)
130
1.19k
    return (NULL);
131
2.86k
  if (name != NULL)
132
0
    *name = pb->name;
133
2.86k
  return (pb);
134
4.06k
}
135
136
/* Get a paste buffer by name. */
137
struct paste_buffer *
138
paste_get_name(const char *name)
139
1.06k
{
140
1.06k
  struct paste_buffer pbfind;
141
142
1.06k
  if (name == NULL || *name == '\0')
143
0
    return (NULL);
144
145
1.06k
  pbfind.name = (char *)name;
146
1.06k
  return (RB_FIND(paste_name_tree, &paste_by_name, &pbfind));
147
1.06k
}
148
149
/* Free a paste buffer. */
150
void
151
paste_free(struct paste_buffer *pb)
152
1.01k
{
153
1.01k
  notify_paste_buffer(pb->name, 1);
154
155
1.01k
  RB_REMOVE(paste_name_tree, &paste_by_name, pb);
156
1.01k
  RB_REMOVE(paste_time_tree, &paste_by_time, pb);
157
1.01k
  if (pb->automatic)
158
1.01k
    paste_num_automatic--;
159
160
1.01k
  free(pb->data);
161
1.01k
  free(pb->name);
162
1.01k
  free(pb);
163
1.01k
}
164
165
/*
166
 * Add an automatic buffer, freeing the oldest automatic item if at limit. Note
167
 * that the caller is responsible for allocating data.
168
 */
169
void
170
paste_add(const char *prefix, char *data, size_t size)
171
1.09k
{
172
1.09k
  struct paste_buffer *pb, *pb1;
173
1.09k
  u_int      limit;
174
175
1.09k
  if (prefix == NULL)
176
1.09k
    prefix = "buffer";
177
178
1.09k
  if (size == 0) {
179
34
    free(data);
180
34
    return;
181
34
  }
182
183
1.06k
  limit = options_get_number(global_options, "buffer-limit");
184
2.07k
  RB_FOREACH_REVERSE_SAFE(pb, paste_time_tree, &paste_by_time, pb1) {
185
2.07k
    if (paste_num_automatic < limit)
186
1.06k
      break;
187
1.01k
    if (pb->automatic)
188
1.01k
      paste_free(pb);
189
1.01k
  }
190
191
1.06k
  pb = xmalloc(sizeof *pb);
192
193
1.06k
  pb->name = NULL;
194
1.06k
  do {
195
1.06k
    free(pb->name);
196
1.06k
    xasprintf(&pb->name, "%s%u", prefix, paste_next_index);
197
1.06k
    paste_next_index++;
198
1.06k
  } while (paste_get_name(pb->name) != NULL);
199
200
1.06k
  pb->data = data;
201
1.06k
  pb->size = size;
202
203
1.06k
  pb->automatic = 1;
204
1.06k
  paste_num_automatic++;
205
206
1.06k
  pb->created = time(NULL);
207
208
1.06k
  pb->order = paste_next_order++;
209
1.06k
  RB_INSERT(paste_name_tree, &paste_by_name, pb);
210
1.06k
  RB_INSERT(paste_time_tree, &paste_by_time, pb);
211
212
1.06k
  notify_paste_buffer(pb->name, 0);
213
1.06k
}
214
215
/* Rename a paste buffer. */
216
int
217
paste_rename(const char *oldname, const char *newname, char **cause)
218
0
{
219
0
  struct paste_buffer *pb, *pb_new;
220
221
0
  if (cause != NULL)
222
0
    *cause = NULL;
223
224
0
  if (oldname == NULL || *oldname == '\0') {
225
0
    if (cause != NULL)
226
0
      *cause = xstrdup("no buffer");
227
0
    return (-1);
228
0
  }
229
0
  if (newname == NULL || *newname == '\0') {
230
0
    if (cause != NULL)
231
0
      *cause = xstrdup("new name is empty");
232
0
    return (-1);
233
0
  }
234
235
0
  pb = paste_get_name(oldname);
236
0
  if (pb == NULL) {
237
0
    if (cause != NULL)
238
0
      xasprintf(cause, "no buffer %s", oldname);
239
0
    return (-1);
240
0
  }
241
242
0
  pb_new = paste_get_name(newname);
243
0
  if (pb_new == pb)
244
0
    return (0);
245
0
  if (pb_new != NULL)
246
0
    paste_free(pb_new);
247
248
0
  RB_REMOVE(paste_name_tree, &paste_by_name, pb);
249
250
0
  free(pb->name);
251
0
  pb->name = xstrdup(newname);
252
253
0
  if (pb->automatic)
254
0
    paste_num_automatic--;
255
0
  pb->automatic = 0;
256
257
0
  RB_INSERT(paste_name_tree, &paste_by_name, pb);
258
259
0
  notify_paste_buffer(oldname, 1);
260
0
  notify_paste_buffer(newname, 0);
261
262
0
  return (0);
263
0
}
264
265
/*
266
 * Add or replace an item in the store. Note that the caller is responsible for
267
 * allocating data.
268
 */
269
int
270
paste_set(char *data, size_t size, const char *name, char **cause)
271
0
{
272
0
  struct paste_buffer *pb, *old;
273
274
0
  if (cause != NULL)
275
0
    *cause = NULL;
276
277
0
  if (size == 0) {
278
0
    free(data);
279
0
    return (0);
280
0
  }
281
0
  if (name == NULL) {
282
0
    paste_add(NULL, data, size);
283
0
    return (0);
284
0
  }
285
286
0
  if (*name == '\0') {
287
0
    if (cause != NULL)
288
0
      *cause = xstrdup("empty buffer name");
289
0
    return (-1);
290
0
  }
291
292
0
  pb = xmalloc(sizeof *pb);
293
294
0
  pb->name = xstrdup(name);
295
296
0
  pb->data = data;
297
0
  pb->size = size;
298
299
0
  pb->automatic = 0;
300
0
  pb->order = paste_next_order++;
301
302
0
  pb->created = time(NULL);
303
304
0
  if ((old = paste_get_name(name)) != NULL)
305
0
    paste_free(old);
306
307
0
  RB_INSERT(paste_name_tree, &paste_by_name, pb);
308
0
  RB_INSERT(paste_time_tree, &paste_by_time, pb);
309
310
0
  notify_paste_buffer(name, 0);
311
312
0
  return (0);
313
0
}
314
315
/* Set paste data without otherwise changing it. */
316
void
317
paste_replace(struct paste_buffer *pb, char *data, size_t size)
318
0
{
319
0
  free(pb->data);
320
0
  pb->data = data;
321
0
  pb->size = size;
322
323
0
  notify_paste_buffer(pb->name, 0);
324
0
}
325
326
/* Convert start of buffer into a nice string. */
327
char *
328
paste_make_sample(struct paste_buffer *pb)
329
0
{
330
0
  char    *buf;
331
0
  size_t     len, used;
332
0
  const int  flags = VIS_OCTAL|VIS_CSTYLE|VIS_TAB|VIS_NL;
333
0
  const size_t   width = 200;
334
335
0
  len = pb->size;
336
0
  if (len > width)
337
0
    len = width;
338
0
  buf = xreallocarray(NULL, len, 4 + 4);
339
340
0
  used = utf8_strvis(buf, pb->data, len, flags);
341
0
  if (pb->size > width || used > width)
342
0
    strlcpy(buf + width, "...", 4);
343
0
  return (buf);
344
0
}