xref: /openbsd-src/usr.bin/tmux/cmd-load-buffer.c (revision a80784a1908d40c54f85da33358867c7b97c05e3)
1 /* $OpenBSD: cmd-load-buffer.c,v 1.17 2011/05/18 08:07:44 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 	"b:", 1, 1,
39 	CMD_BUFFER_USAGE " path",
40 	0,
41 	NULL,
42 	NULL,
43 	cmd_load_buffer_exec
44 };
45 
46 int
47 cmd_load_buffer_exec(struct cmd *self, struct cmd_ctx *ctx)
48 {
49 	struct args	*args = self->args;
50 	struct client	*c = ctx->cmdclient;
51 	FILE		*f;
52 	const char	*path;
53 	char		*pdata, *new_pdata, *cause;
54 	size_t		 psize;
55 	u_int		 limit;
56 	int		 ch, buffer;
57 	int		*buffer_ptr;
58 
59 	if (!args_has(args, 'b'))
60 		buffer = -1;
61 	else {
62 		buffer = args_strtonum(args, 'b', 0, INT_MAX, &cause);
63 		if (cause != NULL) {
64 			ctx->error(ctx, "buffer %s", cause);
65 			xfree(cause);
66 			return (-1);
67 		}
68 	}
69 
70 	path = args->argv[0];
71 	if (strcmp(path, "-") == 0) {
72 		if (c == NULL) {
73 			ctx->error(ctx, "%s: can't read from stdin", path);
74 			return (-1);
75 		}
76 		if (c->flags & CLIENT_TERMINAL) {
77 			ctx->error(ctx, "%s: stdin is a tty", path);
78 			return (-1);
79 		}
80 		if (c->stdin_fd == -1) {
81 			ctx->error(ctx, "%s: can't read from stdin", path);
82 			return (-1);
83 		}
84 
85 		buffer_ptr = xmalloc(sizeof *buffer_ptr);
86 		*buffer_ptr = buffer;
87 
88 		c->stdin_data = buffer_ptr;
89 		c->stdin_callback = cmd_load_buffer_callback;
90 
91 		c->references++;
92 		bufferevent_enable(c->stdin_event, EV_READ);
93 		return (1);
94 	}
95 
96 	if ((f = fopen(path, "rb")) == NULL) {
97 		ctx->error(ctx, "%s: %s", path, strerror(errno));
98 		return (-1);
99 	}
100 
101 	pdata = NULL;
102 	psize = 0;
103 	while ((ch = getc(f)) != EOF) {
104 		/* Do not let the server die due to memory exhaustion. */
105 		if ((new_pdata = realloc(pdata, psize + 2)) == NULL) {
106 			ctx->error(ctx, "realloc error: %s", strerror(errno));
107 			goto error;
108 		}
109 		pdata = new_pdata;
110 		pdata[psize++] = ch;
111 	}
112 	if (ferror(f)) {
113 		ctx->error(ctx, "%s: read error", path);
114 		goto error;
115 	}
116 	if (pdata != NULL)
117 		pdata[psize] = '\0';
118 
119 	fclose(f);
120 
121 	limit = options_get_number(&global_options, "buffer-limit");
122 	if (buffer == -1) {
123 		paste_add(&global_buffers, pdata, psize, limit);
124 		return (0);
125 	}
126 	if (paste_replace(&global_buffers, buffer, pdata, psize) != 0) {
127 		ctx->error(ctx, "no buffer %d", buffer);
128 		return (-1);
129 	}
130 
131 	return (0);
132 
133 error:
134 	if (pdata != NULL)
135 		xfree(pdata);
136 	if (f != NULL)
137 		fclose(f);
138 	return (-1);
139 }
140 
141 void
142 cmd_load_buffer_callback(struct client *c, void *data)
143 {
144 	int	*buffer = data;
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 	psize = EVBUFFER_LENGTH(c->stdin_event->input);
156 	if (psize == 0 || (pdata = malloc(psize + 1)) == NULL) {
157 		xfree(data);
158 		return;
159 	}
160 	bufferevent_read(c->stdin_event, pdata, psize);
161 	pdata[psize] = '\0';
162 
163 	limit = options_get_number(&global_options, "buffer-limit");
164 	if (*buffer == -1)
165 		paste_add(&global_buffers, pdata, psize, limit);
166 	else if (paste_replace(&global_buffers, *buffer, pdata, psize) != 0) {
167 		/* No context so can't use server_client_msg_error. */
168 		evbuffer_add_printf(
169 		    c->stderr_event->output, "no buffer %d\n", *buffer);
170 		bufferevent_enable(c->stderr_event, EV_WRITE);
171 	}
172 
173 	xfree(data);
174 }
175