Home | History | Annotate | Line # | Download | only in dist
      1 /* $OpenBSD$ */
      2 
      3 /*
      4  * Copyright (c) 2007 Nicholas Marriott <nicholas.marriott (at) 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 {
     62 	return (strcmp(a->name, b->name));
     63 }
     64 
     65 static 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 order. */
     83 u_int
     84 paste_buffer_order(struct paste_buffer *pb)
     85 {
     86 	return (pb->order);
     87 }
     88 
     89 /* Get paste buffer created. */
     90 time_t
     91 paste_buffer_created(struct paste_buffer *pb)
     92 {
     93 	return (pb->created);
     94 }
     95 
     96 /* Get paste buffer data. */
     97 const char *
     98 paste_buffer_data(struct paste_buffer *pb, size_t *size)
     99 {
    100 	if (size != NULL)
    101 		*size = pb->size;
    102 	return (pb->data);
    103 }
    104 
    105 /* Walk paste buffers by time. */
    106 struct paste_buffer *
    107 paste_walk(struct paste_buffer *pb)
    108 {
    109 	if (pb == NULL)
    110 		return (RB_MIN(paste_time_tree, &paste_by_time));
    111 	return (RB_NEXT(paste_time_tree, &paste_by_time, pb));
    112 }
    113 
    114 int
    115 paste_is_empty(void)
    116 {
    117 	return RB_ROOT(&paste_by_time) == NULL;
    118 }
    119 
    120 /* Get the most recent automatic buffer. */
    121 struct paste_buffer *
    122 paste_get_top(const char **name)
    123 {
    124 	struct paste_buffer	*pb;
    125 
    126 	pb = RB_MIN(paste_time_tree, &paste_by_time);
    127 	while (pb != NULL && !pb->automatic)
    128 		pb = RB_NEXT(paste_time_tree, &paste_by_time, pb);
    129 	if (pb == NULL)
    130 		return (NULL);
    131 	if (name != NULL)
    132 		*name = pb->name;
    133 	return (pb);
    134 }
    135 
    136 /* Get a paste buffer by name. */
    137 struct paste_buffer *
    138 paste_get_name(const char *name)
    139 {
    140 	struct paste_buffer	pbfind;
    141 
    142 	if (name == NULL || *name == '\0')
    143 		return (NULL);
    144 
    145 	pbfind.name = __UNCONST(name);
    146 	return (RB_FIND(paste_name_tree, &paste_by_name, &pbfind));
    147 }
    148 
    149 /* Free a paste buffer. */
    150 void
    151 paste_free(struct paste_buffer *pb)
    152 {
    153 	notify_paste_buffer(pb->name, 1);
    154 
    155 	RB_REMOVE(paste_name_tree, &paste_by_name, pb);
    156 	RB_REMOVE(paste_time_tree, &paste_by_time, pb);
    157 	if (pb->automatic)
    158 		paste_num_automatic--;
    159 
    160 	free(pb->data);
    161 	free(pb->name);
    162 	free(pb);
    163 }
    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 {
    172 	struct paste_buffer	*pb, *pb1;
    173 	u_int			 limit;
    174 
    175 	if (prefix == NULL)
    176 		prefix = "buffer";
    177 
    178 	if (size == 0) {
    179 		free(data);
    180 		return;
    181 	}
    182 
    183 	limit = options_get_number(global_options, "buffer-limit");
    184 	RB_FOREACH_REVERSE_SAFE(pb, paste_time_tree, &paste_by_time, pb1) {
    185 		if (paste_num_automatic < limit)
    186 			break;
    187 		if (pb->automatic)
    188 			paste_free(pb);
    189 	}
    190 
    191 	pb = xmalloc(sizeof *pb);
    192 
    193 	pb->name = NULL;
    194 	do {
    195 		free(pb->name);
    196 		xasprintf(&pb->name, "%s%u", prefix, paste_next_index);
    197 		paste_next_index++;
    198 	} while (paste_get_name(pb->name) != NULL);
    199 
    200 	pb->data = data;
    201 	pb->size = size;
    202 
    203 	pb->automatic = 1;
    204 	paste_num_automatic++;
    205 
    206 	pb->created = time(NULL);
    207 
    208 	pb->order = paste_next_order++;
    209 	RB_INSERT(paste_name_tree, &paste_by_name, pb);
    210 	RB_INSERT(paste_time_tree, &paste_by_time, pb);
    211 
    212 	notify_paste_buffer(pb->name, 0);
    213 }
    214 
    215 /* Rename a paste buffer. */
    216 int
    217 paste_rename(const char *oldname, const char *newname, char **cause)
    218 {
    219 	struct paste_buffer	*pb, *pb_new;
    220 
    221 	if (cause != NULL)
    222 		*cause = NULL;
    223 
    224 	if (oldname == NULL || *oldname == '\0') {
    225 		if (cause != NULL)
    226 			*cause = xstrdup("no buffer");
    227 		return (-1);
    228 	}
    229 	if (newname == NULL || *newname == '\0') {
    230 		if (cause != NULL)
    231 			*cause = xstrdup("new name is empty");
    232 		return (-1);
    233 	}
    234 
    235 	pb = paste_get_name(oldname);
    236 	if (pb == NULL) {
    237 		if (cause != NULL)
    238 			xasprintf(cause, "no buffer %s", oldname);
    239 		return (-1);
    240 	}
    241 
    242 	pb_new = paste_get_name(newname);
    243 	if (pb_new == pb)
    244 		return (0);
    245 	if (pb_new != NULL)
    246 		paste_free(pb_new);
    247 
    248 	RB_REMOVE(paste_name_tree, &paste_by_name, pb);
    249 
    250 	free(pb->name);
    251 	pb->name = xstrdup(newname);
    252 
    253 	if (pb->automatic)
    254 		paste_num_automatic--;
    255 	pb->automatic = 0;
    256 
    257 	RB_INSERT(paste_name_tree, &paste_by_name, pb);
    258 
    259 	notify_paste_buffer(oldname, 1);
    260 	notify_paste_buffer(newname, 0);
    261 
    262 	return (0);
    263 }
    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 {
    272 	struct paste_buffer	*pb, *old;
    273 
    274 	if (cause != NULL)
    275 		*cause = NULL;
    276 
    277 	if (size == 0) {
    278 		free(data);
    279 		return (0);
    280 	}
    281 	if (name == NULL) {
    282 		paste_add(NULL, data, size);
    283 		return (0);
    284 	}
    285 
    286 	if (*name == '\0') {
    287 		if (cause != NULL)
    288 			*cause = xstrdup("empty buffer name");
    289 		return (-1);
    290 	}
    291 
    292 	pb = xmalloc(sizeof *pb);
    293 
    294 	pb->name = xstrdup(name);
    295 
    296 	pb->data = data;
    297 	pb->size = size;
    298 
    299 	pb->automatic = 0;
    300 	pb->order = paste_next_order++;
    301 
    302 	pb->created = time(NULL);
    303 
    304 	if ((old = paste_get_name(name)) != NULL)
    305 		paste_free(old);
    306 
    307 	RB_INSERT(paste_name_tree, &paste_by_name, pb);
    308 	RB_INSERT(paste_time_tree, &paste_by_time, pb);
    309 
    310 	notify_paste_buffer(name, 0);
    311 
    312 	return (0);
    313 }
    314 
    315 /* Set paste data without otherwise changing it. */
    316 void
    317 paste_replace(struct paste_buffer *pb, char *data, size_t size)
    318 {
    319 	free(pb->data);
    320 	pb->data = data;
    321 	pb->size = size;
    322 
    323 	notify_paste_buffer(pb->name, 0);
    324 }
    325 
    326 /* Convert start of buffer into a nice string. */
    327 char *
    328 paste_make_sample(struct paste_buffer *pb)
    329 {
    330 	char		*buf;
    331 	size_t		 len, used;
    332 	const int	 flags = VIS_OCTAL|VIS_CSTYLE|VIS_TAB|VIS_NL;
    333 	const size_t	 width = 200;
    334 
    335 	len = pb->size;
    336 	if (len > width)
    337 		len = width;
    338 	buf = xreallocarray(NULL, len, 4 + 4);
    339 
    340 	used = utf8_strvis(buf, pb->data, len, flags);
    341 	if (pb->size > width || used > width)
    342 		strlcpy(buf + width, "...", 4);
    343 	return (buf);
    344 }
    345