Annotation of src/usr.bin/tmux/cmd.c, Revision 1.2
1.2 ! nicm 1: /* $OpenBSD: cmd.c,v 1.1 2009/06/01 22:58:49 nicm Exp $ */
1.1 nicm 2:
3: /*
4: * Copyright (c) 2007 Nicholas Marriott <nicm@users.sourceforge.net>
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: #include <sys/time.h>
21:
22: #include <stdlib.h>
23: #include <string.h>
24: #include <unistd.h>
25:
26: #include "tmux.h"
27:
28: const struct cmd_entry *cmd_table[] = {
29: &cmd_attach_session_entry,
30: &cmd_bind_key_entry,
31: &cmd_break_pane_entry,
32: &cmd_choose_session_entry,
33: &cmd_choose_window_entry,
34: &cmd_clear_history_entry,
35: &cmd_clock_mode_entry,
36: &cmd_command_prompt_entry,
37: &cmd_confirm_before_entry,
38: &cmd_copy_buffer_entry,
39: &cmd_copy_mode_entry,
40: &cmd_delete_buffer_entry,
41: &cmd_detach_client_entry,
42: &cmd_down_pane_entry,
43: &cmd_find_window_entry,
44: &cmd_has_session_entry,
45: &cmd_kill_pane_entry,
46: &cmd_kill_server_entry,
47: &cmd_kill_session_entry,
48: &cmd_kill_window_entry,
49: &cmd_last_window_entry,
50: &cmd_link_window_entry,
51: &cmd_list_buffers_entry,
52: &cmd_list_clients_entry,
53: &cmd_list_commands_entry,
54: &cmd_list_keys_entry,
55: &cmd_list_sessions_entry,
56: &cmd_list_windows_entry,
57: &cmd_load_buffer_entry,
58: &cmd_lock_server_entry,
59: &cmd_move_window_entry,
60: &cmd_new_session_entry,
61: &cmd_new_window_entry,
62: &cmd_next_layout_entry,
63: &cmd_next_window_entry,
64: &cmd_paste_buffer_entry,
65: &cmd_previous_layout_entry,
66: &cmd_previous_window_entry,
67: &cmd_refresh_client_entry,
68: &cmd_rename_session_entry,
69: &cmd_rename_window_entry,
70: &cmd_resize_pane_entry,
71: &cmd_respawn_window_entry,
72: &cmd_rotate_window_entry,
73: &cmd_save_buffer_entry,
74: &cmd_scroll_mode_entry,
75: &cmd_select_layout_entry,
76: &cmd_select_pane_entry,
77: &cmd_select_prompt_entry,
78: &cmd_select_window_entry,
79: &cmd_send_keys_entry,
80: &cmd_send_prefix_entry,
81: &cmd_server_info_entry,
82: &cmd_set_buffer_entry,
83: &cmd_set_option_entry,
84: &cmd_set_password_entry,
85: &cmd_set_window_option_entry,
86: &cmd_show_buffer_entry,
87: &cmd_show_options_entry,
88: &cmd_show_window_options_entry,
89: &cmd_source_file_entry,
90: &cmd_split_window_entry,
91: &cmd_start_server_entry,
92: &cmd_suspend_client_entry,
93: &cmd_swap_pane_entry,
94: &cmd_swap_window_entry,
95: &cmd_switch_client_entry,
96: &cmd_unbind_key_entry,
97: &cmd_unlink_window_entry,
98: &cmd_up_pane_entry,
99: NULL
100: };
101:
102: struct cmd *
103: cmd_parse(int argc, char **argv, char **cause)
104: {
105: const struct cmd_entry **entryp, *entry;
106: struct cmd *cmd;
107: char s[BUFSIZ];
108: int opt;
109:
110: *cause = NULL;
1.2 ! nicm 111: if (argc == 0) {
! 112: xasprintf(cause, "no command");
1.1 nicm 113: return (NULL);
1.2 ! nicm 114: }
1.1 nicm 115:
116: entry = NULL;
117: for (entryp = cmd_table; *entryp != NULL; entryp++) {
118: if ((*entryp)->alias != NULL &&
119: strcmp((*entryp)->alias, argv[0]) == 0) {
120: entry = *entryp;
121: break;
122: }
123:
124: if (strncmp((*entryp)->name, argv[0], strlen(argv[0])) != 0)
125: continue;
126: if (entry != NULL)
127: goto ambiguous;
128: entry = *entryp;
129:
130: /* Bail now if an exact match. */
131: if (strcmp(entry->name, argv[0]) == 0)
132: break;
133: }
134: if (entry == NULL) {
135: xasprintf(cause, "unknown command: %s", argv[0]);
136: return (NULL);
137: }
138:
139: optreset = 1;
140: optind = 1;
141: if (entry->parse == NULL) {
142: while ((opt = getopt(argc, argv, "")) != -1) {
143: switch (opt) {
144: default:
145: goto usage;
146: }
147: }
148: argc -= optind;
149: argv += optind;
150: if (argc != 0)
151: goto usage;
152: }
153:
154: cmd = xmalloc(sizeof *cmd);
155: cmd->entry = entry;
156: cmd->data = NULL;
157: if (entry->parse != NULL) {
158: if (entry->parse(cmd, argc, argv, cause) != 0) {
159: xfree(cmd);
160: return (NULL);
161: }
162: }
163: return (cmd);
164:
165: ambiguous:
166: *s = '\0';
167: for (entryp = cmd_table; *entryp != NULL; entryp++) {
168: if (strncmp((*entryp)->name, argv[0], strlen(argv[0])) != 0)
169: continue;
170: if (strlcat(s, (*entryp)->name, sizeof s) >= sizeof s)
171: break;
172: if (strlcat(s, ", ", sizeof s) >= sizeof s)
173: break;
174: }
175: s[strlen(s) - 2] = '\0';
176: xasprintf(cause, "ambiguous command: %s, could be: %s", argv[0], s);
177: return (NULL);
178:
179: usage:
180: xasprintf(cause, "usage: %s %s", entry->name, entry->usage);
181: return (NULL);
182: }
183:
184: int
185: cmd_exec(struct cmd *cmd, struct cmd_ctx *ctx)
186: {
187: if (server_locked) {
188: ctx->error(ctx, "server is locked");
189: return (-1);
190: }
191: return (cmd->entry->exec(cmd, ctx));
192: }
193:
194: void
195: cmd_send(struct cmd *cmd, struct buffer *b)
196: {
197: const struct cmd_entry **entryp;
198: u_int n;
199:
200: n = 0;
201: for (entryp = cmd_table; *entryp != NULL; entryp++) {
202: if (*entryp == cmd->entry)
203: break;
204: n++;
205: }
206: if (*entryp == NULL)
207: fatalx("command not found");
208:
209: buffer_write(b, &n, sizeof n);
210:
211: if (cmd->entry->send != NULL)
212: cmd->entry->send(cmd, b);
213: }
214:
215: struct cmd *
216: cmd_recv(struct buffer *b)
217: {
218: const struct cmd_entry **entryp;
219: struct cmd *cmd;
220: u_int m, n;
221:
222: buffer_read(b, &m, sizeof m);
223:
224: n = 0;
225: for (entryp = cmd_table; *entryp != NULL; entryp++) {
226: if (n == m)
227: break;
228: n++;
229: }
230: if (*entryp == NULL)
231: fatalx("command not found");
232:
233: cmd = xmalloc(sizeof *cmd);
234: cmd->entry = *entryp;
235:
236: if (cmd->entry->recv != NULL)
237: cmd->entry->recv(cmd, b);
238: return (cmd);
239: }
240:
241: void
242: cmd_free(struct cmd *cmd)
243: {
244: if (cmd->data != NULL && cmd->entry->free != NULL)
245: cmd->entry->free(cmd);
246: xfree(cmd);
247: }
248:
249: size_t
250: cmd_print(struct cmd *cmd, char *buf, size_t len)
251: {
252: if (cmd->entry->print == NULL) {
253: return (xsnprintf(buf, len, "%s", cmd->entry->name));
254: }
255: return (cmd->entry->print(cmd, buf, len));
256: }
257:
258: void
259: cmd_send_string(struct buffer *b, const char *s)
260: {
261: size_t n;
262:
263: if (s == NULL) {
264: n = 0;
265: buffer_write(b, &n, sizeof n);
266: return;
267: }
268:
269: n = strlen(s) + 1;
270: buffer_write(b, &n, sizeof n);
271:
272: buffer_write(b, s, n);
273: }
274:
275: char *
276: cmd_recv_string(struct buffer *b)
277: {
278: char *s;
279: size_t n;
280:
281: buffer_read(b, &n, sizeof n);
282:
283: if (n == 0)
284: return (NULL);
285:
286: s = xmalloc(n);
287: buffer_read(b, s, n);
288: s[n - 1] = '\0';
289:
290: return (s);
291: }
292:
293: struct session *
294: cmd_current_session(struct cmd_ctx *ctx)
295: {
296: struct msg_command_data *data = ctx->msgdata;
297: struct timeval *tv;
298: struct session *s, *newest = NULL;
299: u_int i;
300:
301: if (ctx->cursession != NULL)
302: return (ctx->cursession);
303:
304: if (data != NULL && data->pid != -1) {
305: if (data->pid != getpid()) {
306: ctx->error(ctx, "wrong server: %ld", (long) data->pid);
307: return (NULL);
308: }
309: if (data->idx > ARRAY_LENGTH(&sessions)) {
310: ctx->error(ctx, "index out of range: %d", data->idx);
311: return (NULL);
312: }
313: if ((s = ARRAY_ITEM(&sessions, data->idx)) == NULL) {
314: ctx->error(ctx, "session doesn't exist: %u", data->idx);
315: return (NULL);
316: }
317: return (s);
318: }
319:
320: tv = NULL;
321: for (i = 0; i < ARRAY_LENGTH(&sessions); i++) {
322: s = ARRAY_ITEM(&sessions, i);
323: if (s != NULL && (tv == NULL || timercmp(&s->tv, tv, >))) {
324: newest = ARRAY_ITEM(&sessions, i);
325: tv = &s->tv;
326: }
327: }
328: return (newest);
329: }
330:
331: struct client *
332: cmd_find_client(struct cmd_ctx *ctx, const char *arg)
333: {
334: struct client *c;
335:
336: if (arg == NULL)
337: c = ctx->curclient;
338: else {
339: if ((c = arg_parse_client(arg)) == NULL) {
340: if (arg != NULL)
341: ctx->error(ctx, "client not found: %s", arg);
342: else
343: ctx->error(ctx, "no client found");
344: }
345: }
346: return (c);
347: }
348:
349: struct session *
350: cmd_find_session(struct cmd_ctx *ctx, const char *arg)
351: {
352: struct session *s;
353:
354: if (arg == NULL)
355: s = cmd_current_session(ctx);
356: else {
357: if ((s = arg_parse_session(arg)) == NULL) {
358: if (arg != NULL)
359: ctx->error(ctx, "session not found: %s", arg);
360: else
361: ctx->error(ctx, "no session found");
362: }
363: }
364: return (s);
365: }
366:
367: struct winlink *
368: cmd_find_window(struct cmd_ctx *ctx, const char *arg, struct session **sp)
369: {
370: struct session *s;
371: struct winlink *wl;
372: int idx;
373:
374: wl = NULL;
375: if (arg_parse_window(arg, &s, &idx) != 0) {
376: ctx->error(ctx, "bad window: %s", arg);
377: return (NULL);
378: }
379: if (s == NULL)
380: s = ctx->cursession;
381: if (s == NULL)
382: s = cmd_current_session(ctx);
383: if (s == NULL)
384: return (NULL);
385: if (sp != NULL)
386: *sp = s;
387:
388: if (idx == -1)
389: wl = s->curw;
390: else
391: wl = winlink_find_by_index(&s->windows, idx);
392: if (wl == NULL)
393: ctx->error(ctx, "window not found: %s:%d", s->name, idx);
394: return (wl);
395: }