xref: /openbsd-src/usr.bin/tmux/cmd-load-buffer.c (revision eca05f1fc0ec6fc9c207193ddcba9f00995f6fdc)
1 /* $OpenBSD: cmd-load-buffer.c,v 1.15 2010/12/30 23:16:18 nicm Exp $ */
2 
3 /*
4  * Copyright (c) 2009 Tiago Cunha <me@tiagocunha.org>
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 <errno.h>
22 #include <stdio.h>
23 #include <stdlib.h>
24 #include <string.h>
25 #include <unistd.h>
26 
27 #include "tmux.h"
28 
29 /*
30  * Loads a session paste buffer from a file.
31  */
32 
33 int	cmd_load_buffer_exec(struct cmd *, struct cmd_ctx *);
34 void	cmd_load_buffer_callback(struct client *, void *);
35 
36 const struct cmd_entry cmd_load_buffer_entry = {
37 	"load-buffer", "loadb",
38 	CMD_BUFFER_USAGE " path",
39 	CMD_ARG1, "",
40 	cmd_buffer_init,
41 	cmd_buffer_parse,
42 	cmd_load_buffer_exec,
43 	cmd_buffer_free,
44 	cmd_buffer_print
45 };
46 
47 int
48 cmd_load_buffer_exec(struct cmd *self, struct cmd_ctx *ctx)
49 {
50 	struct cmd_buffer_data	*data = self->data;
51 	struct client		*c = ctx->cmdclient;
52 	FILE			*f;
53 	char		      	*pdata, *new_pdata;
54 	size_t			 psize;
55 	u_int			 limit;
56 	int			 ch;
57 
58 	if (strcmp(data->arg, "-") == 0) {
59 		if (c == NULL) {
60 			ctx->error(ctx, "%s: can't read from stdin", data->arg);
61 			return (-1);
62 		}
63 		if (c->flags & CLIENT_TERMINAL) {
64 			ctx->error(ctx, "%s: stdin is a tty", data->arg);
65 			return (-1);
66 		}
67 		if (c->stdin_fd == -1) {
68 			ctx->error(ctx, "%s: can't read from stdin", data->arg);
69 			return (-1);
70 		}
71 
72 		c->stdin_data = &data->buffer;
73 		c->stdin_callback = cmd_load_buffer_callback;
74 
75 		c->references++;
76 		bufferevent_enable(c->stdin_event, EV_READ);
77 		return (1);
78 	}
79 
80 	if ((f = fopen(data->arg, "rb")) == NULL) {
81 		ctx->error(ctx, "%s: %s", data->arg, strerror(errno));
82 		return (-1);
83 	}
84 
85 	pdata = NULL;
86 	psize = 0;
87 	while ((ch = getc(f)) != EOF) {
88 		/* Do not let the server die due to memory exhaustion. */
89 		if ((new_pdata = realloc(pdata, psize + 2)) == NULL) {
90 			ctx->error(ctx, "realloc error: %s", strerror(errno));
91 			goto error;
92 		}
93 		pdata = new_pdata;
94 		pdata[psize++] = ch;
95 	}
96 	if (ferror(f)) {
97 		ctx->error(ctx, "%s: read error", data->arg);
98 		goto error;
99 	}
100 	if (pdata != NULL)
101 		pdata[psize] = '\0';
102 
103 	fclose(f);
104 
105 	limit = options_get_number(&global_options, "buffer-limit");
106 	if (data->buffer == -1) {
107 		paste_add(&global_buffers, pdata, psize, limit);
108 		return (0);
109 	}
110 	if (paste_replace(&global_buffers, data->buffer, pdata, psize) != 0) {
111 		ctx->error(ctx, "no buffer %d", data->buffer);
112 		return (-1);
113 	}
114 
115 	return (0);
116 
117 error:
118 	if (pdata != NULL)
119 		xfree(pdata);
120 	if (f != NULL)
121 		fclose(f);
122 	return (-1);
123 }
124 
125 void
126 cmd_load_buffer_callback(struct client *c, void *data)
127 {
128 	char	*pdata;
129 	size_t	 psize;
130 	u_int	 limit;
131 	int	*buffer = data;
132 
133 	/*
134 	 * Event callback has already checked client is not dead and reduced
135 	 * its reference count. But tell it to exit.
136 	 */
137 	c->flags |= CLIENT_EXIT;
138 
139 	psize = EVBUFFER_LENGTH(c->stdin_event->input);
140 	if (psize == 0)
141 		return;
142 
143 	pdata = malloc(psize + 1);
144 	if (pdata == NULL)
145 		return;
146 	bufferevent_read(c->stdin_event, pdata, psize);
147 	pdata[psize] = '\0';
148 
149 	limit = options_get_number(&global_options, "buffer-limit");
150 	if (*buffer == -1)
151 		paste_add(&global_buffers, pdata, psize, limit);
152 	else if (paste_replace(&global_buffers, *buffer, pdata, psize) != 0) {
153 		/* No context so can't use server_client_msg_error. */
154 		evbuffer_add_printf(
155 		    c->stderr_event->output, "no buffer %d\n", *buffer);
156 		bufferevent_enable(c->stderr_event, EV_WRITE);
157 	}
158 }
159