GCC Code Coverage Report
Directory: ./ Exec Total Coverage
File: usr.bin/tmux/cmd-queue.c Lines: 0 217 0.0 %
Date: 2017-11-07 Branches: 0 107 0.0 %

Line Branch Exec Source
1
/* $OpenBSD: cmd-queue.c,v 1.58 2017/08/30 10:33:57 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
/* Global command queue. */
29
static struct cmdq_list global_queue = TAILQ_HEAD_INITIALIZER(global_queue);
30
31
/* Get command queue name. */
32
static const char *
33
cmdq_name(struct client *c)
34
{
35
	static char	s[32];
36
37
	if (c == NULL)
38
		return ("<global>");
39
	xsnprintf(s, sizeof s, "<%p>", c);
40
	return (s);
41
}
42
43
/* Get command queue from client. */
44
static struct cmdq_list *
45
cmdq_get(struct client *c)
46
{
47
	if (c == NULL)
48
		return (&global_queue);
49
	return (&c->queue);
50
}
51
52
/* Append an item. */
53
void
54
cmdq_append(struct client *c, struct cmdq_item *item)
55
{
56
	struct cmdq_list	*queue = cmdq_get(c);
57
	struct cmdq_item	*next;
58
59
	do {
60
		next = item->next;
61
		item->next = NULL;
62
63
		if (c != NULL)
64
			c->references++;
65
		item->client = c;
66
67
		item->queue = queue;
68
		TAILQ_INSERT_TAIL(queue, item, entry);
69
70
		item = next;
71
	} while (item != NULL);
72
}
73
74
/* Insert an item. */
75
void
76
cmdq_insert_after(struct cmdq_item *after, struct cmdq_item *item)
77
{
78
	struct client		*c = after->client;
79
	struct cmdq_list	*queue = after->queue;
80
	struct cmdq_item	*next;
81
82
	do {
83
		next = item->next;
84
		item->next = NULL;
85
86
		if (c != NULL)
87
			c->references++;
88
		item->client = c;
89
90
		item->queue = queue;
91
		if (after->next != NULL)
92
			TAILQ_INSERT_AFTER(queue, after->next, item, entry);
93
		else
94
			TAILQ_INSERT_AFTER(queue, after, item, entry);
95
		after->next = item;
96
97
		item = next;
98
	} while (item != NULL);
99
}
100
101
/* Remove an item. */
102
static void
103
cmdq_remove(struct cmdq_item *item)
104
{
105
	if (item->shared != NULL && --item->shared->references == 0) {
106
		if (item->shared->formats != NULL)
107
			format_free(item->shared->formats);
108
		free(item->shared);
109
	}
110
111
	if (item->client != NULL)
112
		server_client_unref(item->client);
113
114
	if (item->type == CMDQ_COMMAND)
115
		cmd_list_free(item->cmdlist);
116
117
	TAILQ_REMOVE(item->queue, item, entry);
118
119
	free((void *)item->name);
120
	free(item);
121
}
122
123
/* Set command group. */
124
static u_int
125
cmdq_next_group(void)
126
{
127
	static u_int	group;
128
129
	return (++group);
130
}
131
132
/* Remove all subsequent items that match this item's group. */
133
static void
134
cmdq_remove_group(struct cmdq_item *item)
135
{
136
	struct cmdq_item	*this, *next;
137
138
	this = TAILQ_NEXT(item, entry);
139
	while (this != NULL) {
140
		next = TAILQ_NEXT(this, entry);
141
		if (this->group == item->group)
142
			cmdq_remove(this);
143
		this = next;
144
	}
145
}
146
147
/* Get a command for the command queue. */
148
struct cmdq_item *
149
cmdq_get_command(struct cmd_list *cmdlist, struct cmd_find_state *current,
150
    struct mouse_event *m, int flags)
151
{
152
	struct cmdq_item	*item, *first = NULL, *last = NULL;
153
	struct cmd		*cmd;
154
	u_int			 group = cmdq_next_group();
155
	char			*tmp;
156
	struct cmdq_shared	*shared;
157
158
	shared = xcalloc(1, sizeof *shared);
159
	if (current != NULL)
160
		cmd_find_copy_state(&shared->current, current);
161
	else
162
		cmd_find_clear_state(&shared->current, 0);
163
	if (m != NULL)
164
		memcpy(&shared->mouse, m, sizeof shared->mouse);
165
166
	TAILQ_FOREACH(cmd, &cmdlist->list, qentry) {
167
		xasprintf(&tmp, "command[%s]", cmd->entry->name);
168
169
		item = xcalloc(1, sizeof *item);
170
		item->name = tmp;
171
		item->type = CMDQ_COMMAND;
172
173
		item->group = group;
174
		item->flags = flags;
175
176
		item->shared = shared;
177
		item->cmdlist = cmdlist;
178
		item->cmd = cmd;
179
180
		shared->references++;
181
		cmdlist->references++;
182
183
		if (first == NULL)
184
			first = item;
185
		if (last != NULL)
186
			last->next = item;
187
		last = item;
188
	}
189
	return (first);
190
}
191
192
/* Fill in flag for a command. */
193
static enum cmd_retval
194
cmdq_find_flag(struct cmdq_item *item, struct cmd_find_state *fs,
195
    const struct cmd_entry_flag *flag)
196
{
197
	const char	*value;
198
199
	if (flag->flag == 0) {
200
		cmd_find_clear_state(fs, 0);
201
		return (CMD_RETURN_NORMAL);
202
	}
203
204
	value = args_get(item->cmd->args, flag->flag);
205
	if (cmd_find_target(fs, item, value, flag->type, flag->flags) != 0) {
206
		cmd_find_clear_state(fs, 0);
207
		return (CMD_RETURN_ERROR);
208
	}
209
	return (CMD_RETURN_NORMAL);
210
}
211
212
/* Fire command on command queue. */
213
static enum cmd_retval
214
cmdq_fire_command(struct cmdq_item *item)
215
{
216
	struct client		*c = item->client;
217
	struct cmd		*cmd = item->cmd;
218
	const struct cmd_entry	*entry = cmd->entry;
219
	enum cmd_retval		 retval;
220
	struct cmd_find_state	*fsp, fs;
221
	int			 flags;
222
223
	flags = !!(cmd->flags & CMD_CONTROL);
224
	cmdq_guard(item, "begin", flags);
225
226
	if (item->client == NULL)
227
		item->client = cmd_find_client(item, NULL, 1);
228
	retval = cmdq_find_flag(item, &item->source, &entry->source);
229
	if (retval == CMD_RETURN_ERROR)
230
		goto out;
231
	retval = cmdq_find_flag(item, &item->target, &entry->target);
232
	if (retval == CMD_RETURN_ERROR)
233
		goto out;
234
235
	retval = entry->exec(cmd, item);
236
	if (retval == CMD_RETURN_ERROR)
237
		goto out;
238
239
	if (entry->flags & CMD_AFTERHOOK) {
240
		if (cmd_find_valid_state(&item->target))
241
			fsp = &item->target;
242
		else if (cmd_find_valid_state(&item->shared->current))
243
			fsp = &item->shared->current;
244
		else if (cmd_find_from_client(&fs, item->client, 0) == 0)
245
			fsp = &fs;
246
		else
247
			goto out;
248
		hooks_insert(fsp->s->hooks, item, fsp, "after-%s", entry->name);
249
	}
250
251
out:
252
	item->client = c;
253
	if (retval == CMD_RETURN_ERROR)
254
		cmdq_guard(item, "error", flags);
255
	else
256
		cmdq_guard(item, "end", flags);
257
	return (retval);
258
}
259
260
/* Get a callback for the command queue. */
261
struct cmdq_item *
262
cmdq_get_callback1(const char *name, cmdq_cb cb, void *data)
263
{
264
	struct cmdq_item	*item;
265
	char			*tmp;
266
267
	xasprintf(&tmp, "callback[%s]", name);
268
269
	item = xcalloc(1, sizeof *item);
270
	item->name = tmp;
271
	item->type = CMDQ_CALLBACK;
272
273
	item->group = 0;
274
	item->flags = 0;
275
276
	item->cb = cb;
277
	item->data = data;
278
279
	return (item);
280
}
281
282
/* Fire callback on callback queue. */
283
static enum cmd_retval
284
cmdq_fire_callback(struct cmdq_item *item)
285
{
286
	return (item->cb(item, item->data));
287
}
288
289
/* Add a format to command queue. */
290
void
291
cmdq_format(struct cmdq_item *item, const char *key, const char *fmt, ...)
292
{
293
	struct cmdq_shared	*shared = item->shared;
294
	va_list			 ap;
295
	char			*value;
296
297
	va_start(ap, fmt);
298
	xvasprintf(&value, fmt, ap);
299
	va_end(ap);
300
301
	if (shared->formats == NULL)
302
		shared->formats = format_create(NULL, NULL, FORMAT_NONE, 0);
303
	format_add(shared->formats, key, "%s", value);
304
305
	free(value);
306
}
307
308
/* Process next item on command queue. */
309
u_int
310
cmdq_next(struct client *c)
311
{
312
	struct cmdq_list	*queue = cmdq_get(c);
313
	const char		*name = cmdq_name(c);
314
	struct cmdq_item	*item;
315
	enum cmd_retval		 retval;
316
	u_int			 items = 0;
317
	static u_int		 number;
318
319
	if (TAILQ_EMPTY(queue)) {
320
		log_debug("%s %s: empty", __func__, name);
321
		return (0);
322
	}
323
	if (TAILQ_FIRST(queue)->flags & CMDQ_WAITING) {
324
		log_debug("%s %s: waiting", __func__, name);
325
		return (0);
326
	}
327
328
	log_debug("%s %s: enter", __func__, name);
329
	for (;;) {
330
		item = TAILQ_FIRST(queue);
331
		if (item == NULL)
332
			break;
333
		log_debug("%s %s: %s (%d), flags %x", __func__, name,
334
		    item->name, item->type, item->flags);
335
336
		/*
337
		 * Any item with the waiting flag set waits until an external
338
		 * event clears the flag (for example, a job - look at
339
		 * run-shell).
340
		 */
341
		if (item->flags & CMDQ_WAITING)
342
			goto waiting;
343
344
		/*
345
		 * Items are only fired once, once the fired flag is set, a
346
		 * waiting flag can only be cleared by an external event.
347
		 */
348
		if (~item->flags & CMDQ_FIRED) {
349
			item->time = time(NULL);
350
			item->number = ++number;
351
352
			switch (item->type) {
353
			case CMDQ_COMMAND:
354
				retval = cmdq_fire_command(item);
355
356
				/*
357
				 * If a command returns an error, remove any
358
				 * subsequent commands in the same group.
359
				 */
360
				if (retval == CMD_RETURN_ERROR)
361
					cmdq_remove_group(item);
362
				break;
363
			case CMDQ_CALLBACK:
364
				retval = cmdq_fire_callback(item);
365
				break;
366
			default:
367
				retval = CMD_RETURN_ERROR;
368
				break;
369
			}
370
			item->flags |= CMDQ_FIRED;
371
372
			if (retval == CMD_RETURN_WAIT) {
373
				item->flags |= CMDQ_WAITING;
374
				goto waiting;
375
			}
376
			items++;
377
		}
378
		cmdq_remove(item);
379
	}
380
381
	log_debug("%s %s: exit (empty)", __func__, name);
382
	return (items);
383
384
waiting:
385
	log_debug("%s %s: exit (wait)", __func__, name);
386
	return (items);
387
}
388
389
/* Print a guard line. */
390
void
391
cmdq_guard(struct cmdq_item *item, const char *guard, int flags)
392
{
393
	struct client	*c = item->client;
394
395
	if (c == NULL || !(c->flags & CLIENT_CONTROL))
396
		return;
397
398
	evbuffer_add_printf(c->stdout_data, "%%%s %ld %u %d\n", guard,
399
	    (long)item->time, item->number, flags);
400
	server_client_push_stdout(c);
401
}
402
403
/* Show message from command. */
404
void
405
cmdq_print(struct cmdq_item *item, const char *fmt, ...)
406
{
407
	struct client	*c = item->client;
408
	struct window	*w;
409
	va_list		 ap;
410
	char		*tmp, *msg;
411
412
	va_start(ap, fmt);
413
414
	if (c == NULL)
415
		/* nothing */;
416
	else if (c->session == NULL || (c->flags & CLIENT_CONTROL)) {
417
		if (~c->flags & CLIENT_UTF8) {
418
			xvasprintf(&tmp, fmt, ap);
419
			msg = utf8_sanitize(tmp);
420
			free(tmp);
421
			evbuffer_add(c->stdout_data, msg, strlen(msg));
422
			free(msg);
423
		} else
424
			evbuffer_add_vprintf(c->stdout_data, fmt, ap);
425
		evbuffer_add(c->stdout_data, "\n", 1);
426
		server_client_push_stdout(c);
427
	} else {
428
		w = c->session->curw->window;
429
		if (w->active->mode != &window_copy_mode) {
430
			window_pane_reset_mode(w->active);
431
			window_pane_set_mode(w->active, &window_copy_mode, NULL,
432
			    NULL);
433
			window_copy_init_for_output(w->active);
434
		}
435
		window_copy_vadd(w->active, fmt, ap);
436
	}
437
438
	va_end(ap);
439
}
440
441
/* Show error from command. */
442
void
443
cmdq_error(struct cmdq_item *item, const char *fmt, ...)
444
{
445
	struct client	*c = item->client;
446
	struct cmd	*cmd = item->cmd;
447
	va_list		 ap;
448
	char		*msg;
449
	size_t		 msglen;
450
	char		*tmp;
451
452
	va_start(ap, fmt);
453
	msglen = xvasprintf(&msg, fmt, ap);
454
	va_end(ap);
455
456
	log_debug("%s: %s", __func__, msg);
457
458
	if (c == NULL)
459
		cfg_add_cause("%s:%u: %s", cmd->file, cmd->line, msg);
460
	else if (c->session == NULL || (c->flags & CLIENT_CONTROL)) {
461
		if (~c->flags & CLIENT_UTF8) {
462
			tmp = msg;
463
			msg = utf8_sanitize(tmp);
464
			free(tmp);
465
			msglen = strlen(msg);
466
		}
467
		evbuffer_add(c->stderr_data, msg, msglen);
468
		evbuffer_add(c->stderr_data, "\n", 1);
469
		server_client_push_stderr(c);
470
		c->retval = 1;
471
	} else {
472
		*msg = toupper((u_char) *msg);
473
		status_message_set(c, "%s", msg);
474
	}
475
476
	free(msg);
477
}