|  | /* $Id$ */ | 
|  |  | 
|  | /* | 
|  | * Copyright (c) 2007 Nicholas Marriott <nicm@users.sourceforge.net> | 
|  | * | 
|  | * Permission to use, copy, modify, and distribute this software for any | 
|  | * purpose with or without fee is hereby granted, provided that the above | 
|  | * copyright notice and this permission notice appear in all copies. | 
|  | * | 
|  | * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES | 
|  | * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF | 
|  | * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR | 
|  | * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES | 
|  | * WHATSOEVER RESULTING FROM LOSS OF MIND, USE, DATA OR PROFITS, WHETHER | 
|  | * IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING | 
|  | * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. | 
|  | */ | 
|  |  | 
|  | #include <sys/types.h> | 
|  | #include <sys/time.h> | 
|  |  | 
|  | #include <stdlib.h> | 
|  | #include <string.h> | 
|  |  | 
|  | #include "tmux.h" | 
|  |  | 
|  | /* | 
|  | * Stack of paste buffers. Note that paste buffer data is not necessarily a C | 
|  | * string! | 
|  | */ | 
|  |  | 
|  | /* Return each item of the stack in turn. */ | 
|  | struct paste_buffer * | 
|  | paste_walk_stack(struct paste_stack *ps, u_int *idx) | 
|  | { | 
|  | struct paste_buffer	*pb; | 
|  |  | 
|  | pb = paste_get_index(ps, *idx); | 
|  | (*idx)++; | 
|  | return (pb); | 
|  | } | 
|  |  | 
|  | /* Get the top item on the stack. */ | 
|  | struct paste_buffer * | 
|  | paste_get_top(struct paste_stack *ps) | 
|  | { | 
|  | if (ARRAY_LENGTH(ps) == 0) | 
|  | return (NULL); | 
|  | return (ARRAY_FIRST(ps)); | 
|  | } | 
|  |  | 
|  | /* Get an item by its index. */ | 
|  | struct paste_buffer * | 
|  | paste_get_index(struct paste_stack *ps, u_int idx) | 
|  | { | 
|  | if (idx >= ARRAY_LENGTH(ps)) | 
|  | return (NULL); | 
|  | return (ARRAY_ITEM(ps, idx)); | 
|  | } | 
|  |  | 
|  | /* Free the top item on the stack. */ | 
|  | int | 
|  | paste_free_top(struct paste_stack *ps) | 
|  | { | 
|  | struct paste_buffer	*pb; | 
|  |  | 
|  | if (ARRAY_LENGTH(ps) == 0) | 
|  | return (-1); | 
|  |  | 
|  | pb = ARRAY_FIRST(ps); | 
|  | ARRAY_REMOVE(ps, 0); | 
|  |  | 
|  | free(pb->data); | 
|  | free(pb); | 
|  |  | 
|  | return (0); | 
|  | } | 
|  |  | 
|  | /* Free an item by index. */ | 
|  | int | 
|  | paste_free_index(struct paste_stack *ps, u_int idx) | 
|  | { | 
|  | struct paste_buffer	*pb; | 
|  |  | 
|  | if (idx >= ARRAY_LENGTH(ps)) | 
|  | return (-1); | 
|  |  | 
|  | pb = ARRAY_ITEM(ps, idx); | 
|  | ARRAY_REMOVE(ps, idx); | 
|  |  | 
|  | free(pb->data); | 
|  | free(pb); | 
|  |  | 
|  | return (0); | 
|  | } | 
|  |  | 
|  | /* | 
|  | * Add an item onto the top of the stack, freeing the bottom if at limit. Note | 
|  | * that the caller is responsible for allocating data. | 
|  | */ | 
|  | void | 
|  | paste_add(struct paste_stack *ps, char *data, size_t size, u_int limit) | 
|  | { | 
|  | struct paste_buffer	*pb; | 
|  |  | 
|  | if (size == 0) | 
|  | return; | 
|  |  | 
|  | while (ARRAY_LENGTH(ps) >= limit) { | 
|  | pb = ARRAY_LAST(ps); | 
|  | free(pb->data); | 
|  | free(pb); | 
|  | ARRAY_TRUNC(ps, 1); | 
|  | } | 
|  |  | 
|  | pb = xmalloc(sizeof *pb); | 
|  | ARRAY_INSERT(ps, 0, pb); | 
|  |  | 
|  | pb->data = data; | 
|  | pb->size = size; | 
|  | } | 
|  |  | 
|  |  | 
|  | /* | 
|  | * Replace an item on the stack. Note that the caller is responsible for | 
|  | * allocating data. | 
|  | */ | 
|  | int | 
|  | paste_replace(struct paste_stack *ps, u_int idx, char *data, size_t size) | 
|  | { | 
|  | struct paste_buffer	*pb; | 
|  |  | 
|  | if (size == 0) { | 
|  | free(data); | 
|  | return (0); | 
|  | } | 
|  |  | 
|  | if (idx >= ARRAY_LENGTH(ps)) | 
|  | return (-1); | 
|  |  | 
|  | pb = ARRAY_ITEM(ps, idx); | 
|  | free(pb->data); | 
|  |  | 
|  | pb->data = data; | 
|  | pb->size = size; | 
|  |  | 
|  | return (0); | 
|  | } | 
|  |  | 
|  | /* Convert a buffer into a visible string. */ | 
|  | char * | 
|  | paste_print(struct paste_buffer *pb, size_t width) | 
|  | { | 
|  | char	*buf; | 
|  | size_t	 len, used; | 
|  |  | 
|  | if (width < 3) | 
|  | width = 3; | 
|  | buf = xmalloc(width * 4 + 1); | 
|  |  | 
|  | len = pb->size; | 
|  | if (len > width) | 
|  | len = width; | 
|  |  | 
|  | used = strvisx(buf, pb->data, len, VIS_OCTAL|VIS_TAB|VIS_NL); | 
|  | if (pb->size > width || used > width) | 
|  | strlcpy(buf + width - 3, "...", 4); | 
|  |  | 
|  | return (buf); | 
|  | } | 
|  |  | 
|  | /* Paste into a window pane, filtering '\n' according to separator. */ | 
|  | void | 
|  | paste_send_pane (struct paste_buffer *pb, struct window_pane *wp, | 
|  | const char *sep, int bracket) | 
|  | { | 
|  | const char	*data = pb->data, *end = data + pb->size, *lf; | 
|  | size_t		 seplen; | 
|  |  | 
|  | if (bracket) | 
|  | bufferevent_write(wp->event, "\033[200~", 6); | 
|  |  | 
|  | seplen = strlen(sep); | 
|  | while ((lf = memchr(data, '\n', end - data)) != NULL) { | 
|  | if (lf != data) | 
|  | bufferevent_write(wp->event, data, lf - data); | 
|  | bufferevent_write(wp->event, sep, seplen); | 
|  | data = lf + 1; | 
|  | } | 
|  |  | 
|  | if (end != data) | 
|  | bufferevent_write(wp->event, data, end - data); | 
|  |  | 
|  | if (bracket) | 
|  | bufferevent_write(wp->event, "\033[201~", 6); | 
|  | } |