GCC Code Coverage Report
Directory: ./ Exec Total Coverage
File: usr.bin/tmux/cmd-queue.c Lines: 0 160 0.0 %
Date: 2016-12-06 Branches: 0 84 0.0 %

Line Branch Exec Source
1
/* $OpenBSD: cmd-queue.c,v 1.36 2016/04/29 14:05:24 nicm Exp $ */
2
3
/*
4
 * Copyright (c) 2013 Nicholas Marriott <nicholas.marriott@gmail.com>
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 <ctype.h>
22
#include <stdlib.h>
23
#include <string.h>
24
#include <time.h>
25
26
#include "tmux.h"
27
28
static enum cmd_retval	cmdq_continue_one(struct cmd_q *);
29
30
/* Create new command queue. */
31
struct cmd_q *
32
cmdq_new(struct client *c)
33
{
34
	struct cmd_q	*cmdq;
35
36
	cmdq = xcalloc(1, sizeof *cmdq);
37
	cmdq->references = 1;
38
	cmdq->flags = 0;
39
40
	cmdq->client = c;
41
	cmdq->client_exit = -1;
42
43
	TAILQ_INIT(&cmdq->queue);
44
	cmdq->item = NULL;
45
	cmdq->cmd = NULL;
46
47
	cmd_find_clear_state(&cmdq->current, NULL, 0);
48
	cmdq->parent = NULL;
49
50
	return (cmdq);
51
}
52
53
/* Free command queue */
54
int
55
cmdq_free(struct cmd_q *cmdq)
56
{
57
	if (--cmdq->references != 0) {
58
		if (cmdq->flags & CMD_Q_DEAD)
59
			return (1);
60
		return (0);
61
	}
62
63
	cmdq_flush(cmdq);
64
	free(cmdq);
65
	return (1);
66
}
67
68
/* Show message from command. */
69
void
70
cmdq_print(struct cmd_q *cmdq, const char *fmt, ...)
71
{
72
	struct client	*c = cmdq->client;
73
	struct window	*w;
74
	va_list		 ap;
75
	char		*tmp, *msg;
76
77
	va_start(ap, fmt);
78
79
	if (c == NULL)
80
		/* nothing */;
81
	else if (c->session == NULL || (c->flags & CLIENT_CONTROL)) {
82
		if (~c->flags & CLIENT_UTF8) {
83
			vasprintf(&tmp, fmt, ap);
84
			msg = utf8_sanitize(tmp);
85
			free(tmp);
86
			evbuffer_add(c->stdout_data, msg, strlen(msg));
87
			free(msg);
88
		} else
89
			evbuffer_add_vprintf(c->stdout_data, fmt, ap);
90
		evbuffer_add(c->stdout_data, "\n", 1);
91
		server_client_push_stdout(c);
92
	} else {
93
		w = c->session->curw->window;
94
		if (w->active->mode != &window_copy_mode) {
95
			window_pane_reset_mode(w->active);
96
			window_pane_set_mode(w->active, &window_copy_mode);
97
			window_copy_init_for_output(w->active);
98
		}
99
		window_copy_vadd(w->active, fmt, ap);
100
	}
101
102
	va_end(ap);
103
}
104
105
/* Show error from command. */
106
void
107
cmdq_error(struct cmd_q *cmdq, const char *fmt, ...)
108
{
109
	struct client	*c = cmdq->client;
110
	struct cmd	*cmd = cmdq->cmd;
111
	va_list		 ap;
112
	char		*msg;
113
	size_t		 msglen;
114
	char		*tmp;
115
116
	va_start(ap, fmt);
117
	msglen = xvasprintf(&msg, fmt, ap);
118
	va_end(ap);
119
120
	if (c == NULL)
121
		cfg_add_cause("%s:%u: %s", cmd->file, cmd->line, msg);
122
	else if (c->session == NULL || (c->flags & CLIENT_CONTROL)) {
123
		if (~c->flags & CLIENT_UTF8) {
124
			tmp = msg;
125
			msg = utf8_sanitize(tmp);
126
			free(tmp);
127
			msglen = strlen(msg);
128
		}
129
		evbuffer_add(c->stderr_data, msg, msglen);
130
		evbuffer_add(c->stderr_data, "\n", 1);
131
		server_client_push_stderr(c);
132
		c->retval = 1;
133
	} else {
134
		*msg = toupper((u_char) *msg);
135
		status_message_set(c, "%s", msg);
136
	}
137
138
	free(msg);
139
}
140
141
/* Print a guard line. */
142
void
143
cmdq_guard(struct cmd_q *cmdq, const char *guard, int flags)
144
{
145
	struct client	*c = cmdq->client;
146
147
	if (c == NULL || !(c->flags & CLIENT_CONTROL))
148
		return;
149
150
	evbuffer_add_printf(c->stdout_data, "%%%s %ld %u %d\n", guard,
151
	    (long) cmdq->time, cmdq->number, flags);
152
	server_client_push_stdout(c);
153
}
154
155
/* Add command list to queue and begin processing if needed. */
156
void
157
cmdq_run(struct cmd_q *cmdq, struct cmd_list *cmdlist, struct mouse_event *m)
158
{
159
	cmdq_append(cmdq, cmdlist, m);
160
161
	if (cmdq->item == NULL) {
162
		cmdq->cmd = NULL;
163
		cmdq_continue(cmdq);
164
	}
165
}
166
167
/* Add command list to queue. */
168
void
169
cmdq_append(struct cmd_q *cmdq, struct cmd_list *cmdlist, struct mouse_event *m)
170
{
171
	struct cmd_q_item	*item;
172
173
	item = xcalloc(1, sizeof *item);
174
	item->cmdlist = cmdlist;
175
	TAILQ_INSERT_TAIL(&cmdq->queue, item, qentry);
176
	cmdlist->references++;
177
178
	if (m != NULL)
179
		memcpy(&item->mouse, m, sizeof item->mouse);
180
	else
181
		item->mouse.valid = 0;
182
}
183
184
/* Process one command. */
185
static enum cmd_retval
186
cmdq_continue_one(struct cmd_q *cmdq)
187
{
188
	struct cmd	*cmd = cmdq->cmd;
189
	const char	*name = cmd->entry->name;
190
	struct session	*s;
191
	struct hooks	*hooks;
192
	enum cmd_retval	 retval;
193
	char		*tmp;
194
	int		 flags = !!(cmd->flags & CMD_CONTROL);
195
196
	tmp = cmd_print(cmd);
197
	log_debug("cmdq %p: %s", cmdq, tmp);
198
	free(tmp);
199
200
	cmdq->time = time(NULL);
201
	cmdq->number++;
202
203
	if (~cmdq->flags & CMD_Q_REENTRY)
204
		cmdq_guard(cmdq, "begin", flags);
205
206
	if (cmd_prepare_state(cmd, cmdq, cmdq->parent) != 0)
207
		goto error;
208
209
	if (~cmdq->flags & CMD_Q_NOHOOKS) {
210
		s = NULL;
211
		if (cmdq->state.tflag.s != NULL)
212
			s = cmdq->state.tflag.s;
213
		else if (cmdq->state.sflag.s != NULL)
214
			s = cmdq->state.sflag.s;
215
		else if (cmdq->state.c != NULL)
216
			s = cmdq->state.c->session;
217
		if (s != NULL)
218
			hooks = s->hooks;
219
		else
220
			hooks = global_hooks;
221
222
		if (~cmdq->flags & CMD_Q_REENTRY) {
223
			cmdq->flags |= CMD_Q_REENTRY;
224
			if (hooks_wait(hooks, cmdq, NULL,
225
			    "before-%s", name) == 0)
226
				return (CMD_RETURN_WAIT);
227
			if (cmd_prepare_state(cmd, cmdq, cmdq->parent) != 0)
228
				goto error;
229
		}
230
	} else
231
		hooks = NULL;
232
	cmdq->flags &= ~CMD_Q_REENTRY;
233
234
	retval = cmd->entry->exec(cmd, cmdq);
235
	if (retval == CMD_RETURN_ERROR)
236
		goto error;
237
238
	if (hooks != NULL && hooks_wait(hooks, cmdq, NULL,
239
	    "after-%s", name) == 0)
240
		retval = CMD_RETURN_WAIT;
241
	cmdq_guard(cmdq, "end", flags);
242
243
	return (retval);
244
245
error:
246
	cmdq_guard(cmdq, "error", flags);
247
	cmdq->flags &= ~CMD_Q_REENTRY;
248
	return (CMD_RETURN_ERROR);
249
}
250
251
/* Continue processing command queue. Returns 1 if finishes empty. */
252
int
253
cmdq_continue(struct cmd_q *cmdq)
254
{
255
	struct client		*c = cmdq->client;
256
	struct cmd_q_item	*next;
257
	enum cmd_retval		 retval;
258
	int			 empty;
259
260
	cmdq->references++;
261
	notify_disable();
262
263
	log_debug("continuing cmdq %p: flags %#x, client %p", cmdq, cmdq->flags,
264
	    c);
265
266
	empty = TAILQ_EMPTY(&cmdq->queue);
267
	if (empty)
268
		goto empty;
269
270
	/*
271
	 * If the command isn't in the middle of running hooks (due to
272
	 * CMD_RETURN_WAIT), move onto the next command; otherwise, leave the
273
	 * state of the queue as it is.
274
	 */
275
	if (~cmdq->flags & CMD_Q_REENTRY) {
276
		if (cmdq->item == NULL) {
277
			cmdq->item = TAILQ_FIRST(&cmdq->queue);
278
			cmdq->cmd = TAILQ_FIRST(&cmdq->item->cmdlist->list);
279
		} else
280
			cmdq->cmd = TAILQ_NEXT(cmdq->cmd, qentry);
281
	}
282
283
	do {
284
		while (cmdq->cmd != NULL) {
285
			retval = cmdq_continue_one(cmdq);
286
			if (retval == CMD_RETURN_ERROR)
287
				break;
288
			if (retval == CMD_RETURN_WAIT)
289
				goto out;
290
			if (retval == CMD_RETURN_STOP) {
291
				cmdq_flush(cmdq);
292
				goto empty;
293
			}
294
			cmdq->cmd = TAILQ_NEXT(cmdq->cmd, qentry);
295
		}
296
		next = TAILQ_NEXT(cmdq->item, qentry);
297
298
		TAILQ_REMOVE(&cmdq->queue, cmdq->item, qentry);
299
		cmd_list_free(cmdq->item->cmdlist);
300
		free(cmdq->item);
301
302
		cmdq->item = next;
303
		if (cmdq->item != NULL)
304
			cmdq->cmd = TAILQ_FIRST(&cmdq->item->cmdlist->list);
305
	} while (cmdq->item != NULL);
306
307
empty:
308
	if (cmdq->client_exit > 0)
309
		cmdq->client->flags |= CLIENT_EXIT;
310
	if (cmdq->emptyfn != NULL)
311
		cmdq->emptyfn(cmdq);
312
	empty = 1;
313
314
out:
315
	notify_enable();
316
	cmdq_free(cmdq);
317
318
	return (empty);
319
}
320
321
/* Flush command queue. */
322
void
323
cmdq_flush(struct cmd_q *cmdq)
324
{
325
	struct cmd_q_item	*item, *item1;
326
327
	TAILQ_FOREACH_SAFE(item, &cmdq->queue, qentry, item1) {
328
		TAILQ_REMOVE(&cmdq->queue, item, qentry);
329
		cmd_list_free(item->cmdlist);
330
		free(item);
331
	}
332
	cmdq->item = NULL;
333
}
334