Coverage Report

Created: 2026-07-16 06:55

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