xref: /netbsd-src/external/bsd/tmux/dist/cmd-load-buffer.c (revision 88fcb00c0357f2d7c1774f86a352637bfda96184)
1 /* $Id: cmd-load-buffer.c,v 1.1.1.1 2011/03/10 09:15:37 jmmv 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_SESSION_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 struct cmd_load_buffer_cdata {
48 	struct session	*session;
49 	int		 buffer;
50 };
51 
52 int
53 cmd_load_buffer_exec(struct cmd *self, struct cmd_ctx *ctx)
54 {
55 	struct cmd_buffer_data		*data = self->data;
56 	struct cmd_load_buffer_cdata	*cdata;
57 	struct session			*s;
58 	struct client			*c = ctx->cmdclient;
59 	FILE				*f;
60 	char		      		*pdata, *new_pdata;
61 	size_t				 psize;
62 	u_int				 limit;
63 	int				 ch;
64 
65 	if ((s = cmd_find_session(ctx, data->target)) == NULL)
66 		return (-1);
67 
68 	if (strcmp(data->arg, "-") == 0) {
69 		if (c == NULL) {
70 			ctx->error(ctx, "%s: can't read from stdin", data->arg);
71 			return (-1);
72 		}
73 		if (c->flags & CLIENT_TERMINAL) {
74 			ctx->error(ctx, "%s: stdin is a tty", data->arg);
75 			return (-1);
76 		}
77 		if (c->stdin_fd == -1) {
78 			ctx->error(ctx, "%s: can't read from stdin", data->arg);
79 			return (-1);
80 		}
81 
82 		cdata = xmalloc(sizeof *cdata);
83 		cdata->session = s;
84 		cdata->session->references++;
85 		cdata->buffer = data->buffer;
86 		c->stdin_data = cdata;
87 		c->stdin_callback = cmd_load_buffer_callback;
88 
89 		c->references++;
90 		bufferevent_enable(c->stdin_event, EV_READ);
91 		return (1);
92 	}
93 
94 	if ((f = fopen(data->arg, "rb")) == NULL) {
95 		ctx->error(ctx, "%s: %s", data->arg, strerror(errno));
96 		return (-1);
97 	}
98 
99 	pdata = NULL;
100 	psize = 0;
101 	while ((ch = getc(f)) != EOF) {
102 		/* Do not let the server die due to memory exhaustion. */
103 		if ((new_pdata = realloc(pdata, psize + 2)) == NULL) {
104 			ctx->error(ctx, "realloc error: %s", strerror(errno));
105 			goto error;
106 		}
107 		pdata = new_pdata;
108 		pdata[psize++] = ch;
109 	}
110 	if (ferror(f)) {
111 		ctx->error(ctx, "%s: read error", data->arg);
112 		goto error;
113 	}
114 	if (pdata != NULL)
115 		pdata[psize] = '\0';
116 
117 	fclose(f);
118 	f = NULL;
119 
120 	limit = options_get_number(&s->options, "buffer-limit");
121 	if (data->buffer == -1) {
122 		paste_add(&s->buffers, pdata, psize, limit);
123 		return (0);
124 	}
125 	if (paste_replace(&s->buffers, data->buffer, pdata, psize) != 0) {
126 		ctx->error(ctx, "no buffer %d", data->buffer);
127 		return (-1);
128 	}
129 
130 	return (0);
131 
132 error:
133 	if (pdata != NULL)
134 		xfree(pdata);
135 	if (f != NULL)
136 		fclose(f);
137 	return (-1);
138 }
139 
140 void
141 cmd_load_buffer_callback(struct client *c, void *data)
142 {
143 	struct cmd_load_buffer_cdata	*cdata = data;
144 	struct session			*s = cdata->session;
145 	char				*pdata;
146 	size_t				 psize;
147 	u_int				 limit;
148 
149 	/*
150 	 * Event callback has already checked client is not dead and reduced
151 	 * its reference count. But tell it to exit.
152 	 */
153 	c->flags |= CLIENT_EXIT;
154 
155 	/* Does the target session still exist? */
156 	if (!session_alive(s))
157 		goto out;
158 
159 	psize = EVBUFFER_LENGTH(c->stdin_event->input);
160 	if (psize == 0)
161 		goto out;
162 
163 	pdata = malloc(psize + 1);
164 	if (pdata == NULL)
165 		goto out;
166 	bufferevent_read(c->stdin_event, pdata, psize);
167 	pdata[psize] = '\0';
168 
169 	limit = options_get_number(&s->options, "buffer-limit");
170 	if (cdata->buffer == -1) {
171 		paste_add(&s->buffers, pdata, psize, limit);
172 		goto out;
173 	}
174 	if (paste_replace(&s->buffers, cdata->buffer, pdata, psize) != 0) {
175 		/* No context so can't use server_client_msg_error. */
176 		evbuffer_add_printf(
177 		    c->stderr_event->output, "no buffer %d\n", cdata->buffer);
178 		bufferevent_enable(c->stderr_event, EV_WRITE);
179 		goto out;
180 	}
181 
182 out:
183 	cdata->session->references--;
184 	xfree(cdata);
185 }
186