| /* $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) | 
 | 		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); | 
 | } |