Annotation of src/usr.bin/tmux/cmd.c, Revision 1.1
1.1 ! nicm 1: /* $OpenBSD$ */
! 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;
! 111: if (argc == 0)
! 112: return (NULL);
! 113:
! 114: entry = NULL;
! 115: for (entryp = cmd_table; *entryp != NULL; entryp++) {
! 116: if ((*entryp)->alias != NULL &&
! 117: strcmp((*entryp)->alias, argv[0]) == 0) {
! 118: entry = *entryp;
! 119: break;
! 120: }
! 121:
! 122: if (strncmp((*entryp)->name, argv[0], strlen(argv[0])) != 0)
! 123: continue;
! 124: if (entry != NULL)
! 125: goto ambiguous;
! 126: entry = *entryp;
! 127:
! 128: /* Bail now if an exact match. */
! 129: if (strcmp(entry->name, argv[0]) == 0)
! 130: break;
! 131: }
! 132: if (entry == NULL) {
! 133: xasprintf(cause, "unknown command: %s", argv[0]);
! 134: return (NULL);
! 135: }
! 136:
! 137: optreset = 1;
! 138: optind = 1;
! 139: if (entry->parse == NULL) {
! 140: while ((opt = getopt(argc, argv, "")) != -1) {
! 141: switch (opt) {
! 142: default:
! 143: goto usage;
! 144: }
! 145: }
! 146: argc -= optind;
! 147: argv += optind;
! 148: if (argc != 0)
! 149: goto usage;
! 150: }
! 151:
! 152: cmd = xmalloc(sizeof *cmd);
! 153: cmd->entry = entry;
! 154: cmd->data = NULL;
! 155: if (entry->parse != NULL) {
! 156: if (entry->parse(cmd, argc, argv, cause) != 0) {
! 157: xfree(cmd);
! 158: return (NULL);
! 159: }
! 160: }
! 161: return (cmd);
! 162:
! 163: ambiguous:
! 164: *s = '\0';
! 165: for (entryp = cmd_table; *entryp != NULL; entryp++) {
! 166: if (strncmp((*entryp)->name, argv[0], strlen(argv[0])) != 0)
! 167: continue;
! 168: if (strlcat(s, (*entryp)->name, sizeof s) >= sizeof s)
! 169: break;
! 170: if (strlcat(s, ", ", sizeof s) >= sizeof s)
! 171: break;
! 172: }
! 173: s[strlen(s) - 2] = '\0';
! 174: xasprintf(cause, "ambiguous command: %s, could be: %s", argv[0], s);
! 175: return (NULL);
! 176:
! 177: usage:
! 178: xasprintf(cause, "usage: %s %s", entry->name, entry->usage);
! 179: return (NULL);
! 180: }
! 181:
! 182: int
! 183: cmd_exec(struct cmd *cmd, struct cmd_ctx *ctx)
! 184: {
! 185: if (server_locked) {
! 186: ctx->error(ctx, "server is locked");
! 187: return (-1);
! 188: }
! 189: return (cmd->entry->exec(cmd, ctx));
! 190: }
! 191:
! 192: void
! 193: cmd_send(struct cmd *cmd, struct buffer *b)
! 194: {
! 195: const struct cmd_entry **entryp;
! 196: u_int n;
! 197:
! 198: n = 0;
! 199: for (entryp = cmd_table; *entryp != NULL; entryp++) {
! 200: if (*entryp == cmd->entry)
! 201: break;
! 202: n++;
! 203: }
! 204: if (*entryp == NULL)
! 205: fatalx("command not found");
! 206:
! 207: buffer_write(b, &n, sizeof n);
! 208:
! 209: if (cmd->entry->send != NULL)
! 210: cmd->entry->send(cmd, b);
! 211: }
! 212:
! 213: struct cmd *
! 214: cmd_recv(struct buffer *b)
! 215: {
! 216: const struct cmd_entry **entryp;
! 217: struct cmd *cmd;
! 218: u_int m, n;
! 219:
! 220: buffer_read(b, &m, sizeof m);
! 221:
! 222: n = 0;
! 223: for (entryp = cmd_table; *entryp != NULL; entryp++) {
! 224: if (n == m)
! 225: break;
! 226: n++;
! 227: }
! 228: if (*entryp == NULL)
! 229: fatalx("command not found");
! 230:
! 231: cmd = xmalloc(sizeof *cmd);
! 232: cmd->entry = *entryp;
! 233:
! 234: if (cmd->entry->recv != NULL)
! 235: cmd->entry->recv(cmd, b);
! 236: return (cmd);
! 237: }
! 238:
! 239: void
! 240: cmd_free(struct cmd *cmd)
! 241: {
! 242: if (cmd->data != NULL && cmd->entry->free != NULL)
! 243: cmd->entry->free(cmd);
! 244: xfree(cmd);
! 245: }
! 246:
! 247: size_t
! 248: cmd_print(struct cmd *cmd, char *buf, size_t len)
! 249: {
! 250: if (cmd->entry->print == NULL) {
! 251: return (xsnprintf(buf, len, "%s", cmd->entry->name));
! 252: }
! 253: return (cmd->entry->print(cmd, buf, len));
! 254: }
! 255:
! 256: void
! 257: cmd_send_string(struct buffer *b, const char *s)
! 258: {
! 259: size_t n;
! 260:
! 261: if (s == NULL) {
! 262: n = 0;
! 263: buffer_write(b, &n, sizeof n);
! 264: return;
! 265: }
! 266:
! 267: n = strlen(s) + 1;
! 268: buffer_write(b, &n, sizeof n);
! 269:
! 270: buffer_write(b, s, n);
! 271: }
! 272:
! 273: char *
! 274: cmd_recv_string(struct buffer *b)
! 275: {
! 276: char *s;
! 277: size_t n;
! 278:
! 279: buffer_read(b, &n, sizeof n);
! 280:
! 281: if (n == 0)
! 282: return (NULL);
! 283:
! 284: s = xmalloc(n);
! 285: buffer_read(b, s, n);
! 286: s[n - 1] = '\0';
! 287:
! 288: return (s);
! 289: }
! 290:
! 291: struct session *
! 292: cmd_current_session(struct cmd_ctx *ctx)
! 293: {
! 294: struct msg_command_data *data = ctx->msgdata;
! 295: struct timeval *tv;
! 296: struct session *s, *newest = NULL;
! 297: u_int i;
! 298:
! 299: if (ctx->cursession != NULL)
! 300: return (ctx->cursession);
! 301:
! 302: if (data != NULL && data->pid != -1) {
! 303: if (data->pid != getpid()) {
! 304: ctx->error(ctx, "wrong server: %ld", (long) data->pid);
! 305: return (NULL);
! 306: }
! 307: if (data->idx > ARRAY_LENGTH(&sessions)) {
! 308: ctx->error(ctx, "index out of range: %d", data->idx);
! 309: return (NULL);
! 310: }
! 311: if ((s = ARRAY_ITEM(&sessions, data->idx)) == NULL) {
! 312: ctx->error(ctx, "session doesn't exist: %u", data->idx);
! 313: return (NULL);
! 314: }
! 315: return (s);
! 316: }
! 317:
! 318: tv = NULL;
! 319: for (i = 0; i < ARRAY_LENGTH(&sessions); i++) {
! 320: s = ARRAY_ITEM(&sessions, i);
! 321: if (s != NULL && (tv == NULL || timercmp(&s->tv, tv, >))) {
! 322: newest = ARRAY_ITEM(&sessions, i);
! 323: tv = &s->tv;
! 324: }
! 325: }
! 326: return (newest);
! 327: }
! 328:
! 329: struct client *
! 330: cmd_find_client(struct cmd_ctx *ctx, const char *arg)
! 331: {
! 332: struct client *c;
! 333:
! 334: if (arg == NULL)
! 335: c = ctx->curclient;
! 336: else {
! 337: if ((c = arg_parse_client(arg)) == NULL) {
! 338: if (arg != NULL)
! 339: ctx->error(ctx, "client not found: %s", arg);
! 340: else
! 341: ctx->error(ctx, "no client found");
! 342: }
! 343: }
! 344: return (c);
! 345: }
! 346:
! 347: struct session *
! 348: cmd_find_session(struct cmd_ctx *ctx, const char *arg)
! 349: {
! 350: struct session *s;
! 351:
! 352: if (arg == NULL)
! 353: s = cmd_current_session(ctx);
! 354: else {
! 355: if ((s = arg_parse_session(arg)) == NULL) {
! 356: if (arg != NULL)
! 357: ctx->error(ctx, "session not found: %s", arg);
! 358: else
! 359: ctx->error(ctx, "no session found");
! 360: }
! 361: }
! 362: return (s);
! 363: }
! 364:
! 365: struct winlink *
! 366: cmd_find_window(struct cmd_ctx *ctx, const char *arg, struct session **sp)
! 367: {
! 368: struct session *s;
! 369: struct winlink *wl;
! 370: int idx;
! 371:
! 372: wl = NULL;
! 373: if (arg_parse_window(arg, &s, &idx) != 0) {
! 374: ctx->error(ctx, "bad window: %s", arg);
! 375: return (NULL);
! 376: }
! 377: if (s == NULL)
! 378: s = ctx->cursession;
! 379: if (s == NULL)
! 380: s = cmd_current_session(ctx);
! 381: if (s == NULL)
! 382: return (NULL);
! 383: if (sp != NULL)
! 384: *sp = s;
! 385:
! 386: if (idx == -1)
! 387: wl = s->curw;
! 388: else
! 389: wl = winlink_find_by_index(&s->windows, idx);
! 390: if (wl == NULL)
! 391: ctx->error(ctx, "window not found: %s:%d", s->name, idx);
! 392: return (wl);
! 393: }