Annotation of embedaddon/tmux/paste.c, revision 1.1
1.1 ! misho 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: {
! 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: /* Get the most recent automatic buffer. */
! 115: struct paste_buffer *
! 116: paste_get_top(const char **name)
! 117: {
! 118: struct paste_buffer *pb;
! 119:
! 120: pb = RB_MIN(paste_time_tree, &paste_by_time);
! 121: if (pb == NULL)
! 122: return (NULL);
! 123: if (name != NULL)
! 124: *name = pb->name;
! 125: return (pb);
! 126: }
! 127:
! 128: /* Get a paste buffer by name. */
! 129: struct paste_buffer *
! 130: paste_get_name(const char *name)
! 131: {
! 132: struct paste_buffer pbfind;
! 133:
! 134: if (name == NULL || *name == '\0')
! 135: return (NULL);
! 136:
! 137: pbfind.name = (char *)name;
! 138: return (RB_FIND(paste_name_tree, &paste_by_name, &pbfind));
! 139: }
! 140:
! 141: /* Free a paste buffer. */
! 142: void
! 143: paste_free(struct paste_buffer *pb)
! 144: {
! 145: RB_REMOVE(paste_name_tree, &paste_by_name, pb);
! 146: RB_REMOVE(paste_time_tree, &paste_by_time, pb);
! 147: if (pb->automatic)
! 148: paste_num_automatic--;
! 149:
! 150: free(pb->data);
! 151: free(pb->name);
! 152: free(pb);
! 153: }
! 154:
! 155: /*
! 156: * Add an automatic buffer, freeing the oldest automatic item if at limit. Note
! 157: * that the caller is responsible for allocating data.
! 158: */
! 159: void
! 160: paste_add(char *data, size_t size)
! 161: {
! 162: struct paste_buffer *pb, *pb1;
! 163: u_int limit;
! 164:
! 165: if (size == 0) {
! 166: free(data);
! 167: return;
! 168: }
! 169:
! 170: limit = options_get_number(global_options, "buffer-limit");
! 171: RB_FOREACH_REVERSE_SAFE(pb, paste_time_tree, &paste_by_time, pb1) {
! 172: if (paste_num_automatic < limit)
! 173: break;
! 174: if (pb->automatic)
! 175: paste_free(pb);
! 176: }
! 177:
! 178: pb = xmalloc(sizeof *pb);
! 179:
! 180: pb->name = NULL;
! 181: do {
! 182: free(pb->name);
! 183: xasprintf(&pb->name, "buffer%04u", paste_next_index);
! 184: paste_next_index++;
! 185: } while (paste_get_name(pb->name) != NULL);
! 186:
! 187: pb->data = data;
! 188: pb->size = size;
! 189:
! 190: pb->automatic = 1;
! 191: paste_num_automatic++;
! 192:
! 193: pb->created = time(NULL);
! 194:
! 195: pb->order = paste_next_order++;
! 196: RB_INSERT(paste_name_tree, &paste_by_name, pb);
! 197: RB_INSERT(paste_time_tree, &paste_by_time, pb);
! 198: }
! 199:
! 200: /* Rename a paste buffer. */
! 201: int
! 202: paste_rename(const char *oldname, const char *newname, char **cause)
! 203: {
! 204: struct paste_buffer *pb, *pb_new;
! 205:
! 206: if (cause != NULL)
! 207: *cause = NULL;
! 208:
! 209: if (oldname == NULL || *oldname == '\0') {
! 210: if (cause != NULL)
! 211: *cause = xstrdup("no buffer");
! 212: return (-1);
! 213: }
! 214: if (newname == NULL || *newname == '\0') {
! 215: if (cause != NULL)
! 216: *cause = xstrdup("new name is empty");
! 217: return (-1);
! 218: }
! 219:
! 220: pb = paste_get_name(oldname);
! 221: if (pb == NULL) {
! 222: if (cause != NULL)
! 223: xasprintf(cause, "no buffer %s", oldname);
! 224: return (-1);
! 225: }
! 226:
! 227: pb_new = paste_get_name(newname);
! 228: if (pb_new != NULL) {
! 229: if (cause != NULL)
! 230: xasprintf(cause, "buffer %s already exists", newname);
! 231: return (-1);
! 232: }
! 233:
! 234: RB_REMOVE(paste_name_tree, &paste_by_name, pb);
! 235:
! 236: free(pb->name);
! 237: pb->name = xstrdup(newname);
! 238:
! 239: if (pb->automatic)
! 240: paste_num_automatic--;
! 241: pb->automatic = 0;
! 242:
! 243: RB_INSERT(paste_name_tree, &paste_by_name, pb);
! 244:
! 245: return (0);
! 246: }
! 247:
! 248: /*
! 249: * Add or replace an item in the store. Note that the caller is responsible for
! 250: * allocating data.
! 251: */
! 252: int
! 253: paste_set(char *data, size_t size, const char *name, char **cause)
! 254: {
! 255: struct paste_buffer *pb, *old;
! 256:
! 257: if (cause != NULL)
! 258: *cause = NULL;
! 259:
! 260: if (size == 0) {
! 261: free(data);
! 262: return (0);
! 263: }
! 264: if (name == NULL) {
! 265: paste_add(data, size);
! 266: return (0);
! 267: }
! 268:
! 269: if (*name == '\0') {
! 270: if (cause != NULL)
! 271: *cause = xstrdup("empty buffer name");
! 272: return (-1);
! 273: }
! 274:
! 275: pb = xmalloc(sizeof *pb);
! 276:
! 277: pb->name = xstrdup(name);
! 278:
! 279: pb->data = data;
! 280: pb->size = size;
! 281:
! 282: pb->automatic = 0;
! 283: pb->order = paste_next_order++;
! 284:
! 285: pb->created = time(NULL);
! 286:
! 287: if ((old = paste_get_name(name)) != NULL)
! 288: paste_free(old);
! 289:
! 290: RB_INSERT(paste_name_tree, &paste_by_name, pb);
! 291: RB_INSERT(paste_time_tree, &paste_by_time, pb);
! 292:
! 293: return (0);
! 294: }
! 295:
! 296: /* Convert start of buffer into a nice string. */
! 297: char *
! 298: paste_make_sample(struct paste_buffer *pb)
! 299: {
! 300: char *buf;
! 301: size_t len, used;
! 302: const int flags = VIS_OCTAL|VIS_TAB|VIS_NL;
! 303: const size_t width = 200;
! 304:
! 305: len = pb->size;
! 306: if (len > width)
! 307: len = width;
! 308: buf = xreallocarray(NULL, len, 4 + 4);
! 309:
! 310: used = utf8_strvis(buf, pb->data, len, flags);
! 311: if (pb->size > width || used > width)
! 312: strlcpy(buf + width, "...", 4);
! 313: return (buf);
! 314: }
FreeBSD-CVSweb <freebsd-cvsweb@FreeBSD.org>