From df716ecc8fadc9ffd6b8c9ee33d062b58895a47b Mon Sep 17 00:00:00 2001 From: Nicholas Marriott Date: Wed, 3 Oct 2007 21:31:07 +0000 Subject: [PATCH] Rewrite command handling to be more generic. Not finished! --- CHANGES | 6 +- Makefile | 9 +- TODO | 5 +- client-msg.c | 17 +- client.c | 93 +++------- cmd-detach-session.c | 137 +++++++++++++++ cmd-list-sessions.c | 70 ++++++++ cmd-new-session.c | 162 +++++++++++++++++ cmd.c | 330 +++++++++++++---------------------- key-bindings.c | 214 +++++++++++++++++++++++ op-list.c | 199 --------------------- op.c | 271 ----------------------------- server-fn.c | 17 +- server-msg.c | 404 ++++++++++--------------------------------- server.c | 6 +- session.c | 10 +- tmux.c | 148 ++++++++++------ tmux.h | 194 ++++++++++----------- 18 files changed, 1049 insertions(+), 1243 deletions(-) create mode 100644 cmd-detach-session.c create mode 100644 cmd-list-sessions.c create mode 100644 cmd-new-session.c create mode 100644 key-bindings.c delete mode 100644 op-list.c delete mode 100644 op.c diff --git a/CHANGES b/CHANGES index 2f84a8c9..e50acaff 100644 --- a/CHANGES +++ b/CHANGES @@ -1,5 +1,9 @@ 03 October 2007 +* (nicm) Rewrite command handling so commands are much more generic and the + same commands are used for command line and keys (although most will probably + need to check how they are called). Currently incomplete (only new/detach/ls + implemented). * (nicm) String number arguments. So you can do: tmux bind ^Q create "blah". * (nicm) Key binding. tmux bind key command [argument] and tmux unbind key. Key names are in a table in key-string.c, plus A is A, ^A is ctrl-A. @@ -99,5 +103,5 @@ (including mutt, emacs). No status bar yet and no key remapping or other customisation. -$Id: CHANGES,v 1.25 2007-10-03 12:34:16 nicm Exp $ +$Id: CHANGES,v 1.26 2007-10-03 21:31:06 nicm Exp $ diff --git a/Makefile b/Makefile index 5da175b0..b1d68369 100644 --- a/Makefile +++ b/Makefile @@ -1,4 +1,4 @@ -# $Id: Makefile,v 1.12 2007-10-03 11:26:34 nicm Exp $ +# $Id: Makefile,v 1.13 2007-10-03 21:31:07 nicm Exp $ .SUFFIXES: .c .o .y .h .PHONY: clean @@ -17,9 +17,10 @@ DEBUG= META?= \002 # C-b SRCS= tmux.c server.c server-msg.c server-fn.c buffer.c buffer-poll.c status.c \ - xmalloc.c xmalloc-debug.c cmd.c input.c input-keys.c screen.c window.c \ - session.c local.c log.c client.c client-msg.c client-fn.c op.c op-list.c \ - key-string.c + xmalloc.c xmalloc-debug.c input.c input-keys.c screen.c window.c \ + session.c local.c log.c client.c client-msg.c client-fn.c key-string.c \ + key-bindings.c \ + cmd.c cmd-new-session.c cmd-detach-session.c cmd-list-sessions.c YACC= yacc -d diff --git a/TODO b/TODO index 9ee064ee..2d696923 100644 --- a/TODO +++ b/TODO @@ -33,8 +33,11 @@ IPC is arse-about-face: too much overhead. 8-byte header for each packet... hrm. already scanning output for \e, could add an extra byte to it for message -- could use bsearch all over the place +- could use bsearch all over the place or get rid of smaller tables (clientmsg) - better errors when creating new windows/sessions (how?) +- commands should have to care less about CMD_KEY +- CLIENT_HOLD sucks +- session with CMD_NOSESSION should be an error -- For 0.1 -------------------------------------------------------------------- - man page diff --git a/client-msg.c b/client-msg.c index 0a17d67e..6bcd8173 100644 --- a/client-msg.c +++ b/client-msg.c @@ -1,4 +1,4 @@ -/* $Id: client-msg.c,v 1.5 2007-10-03 10:18:31 nicm Exp $ */ +/* $Id: client-msg.c,v 1.6 2007-10-03 21:31:07 nicm Exp $ */ /* * Copyright (c) 2007 Nicholas Marriott @@ -39,7 +39,6 @@ struct client_msg client_msg_table[] = { { MSG_DATA, client_msg_fn_data }, { MSG_DETACH, client_msg_fn_detach }, { MSG_ERROR, client_msg_fn_error }, - { MSG_OKAY, client_msg_fn_okay }, { MSG_PAUSE, client_msg_fn_pause }, }; #define NCLIENTMSG (sizeof client_msg_table / sizeof client_msg_table[0]) @@ -73,7 +72,6 @@ client_msg_dispatch(struct client_ctx *cctx, char **error) } } -/* Data message from server. */ int client_msg_fn_data( struct hdr *hdr, struct client_ctx *cctx, unused char **error) @@ -82,7 +80,6 @@ client_msg_fn_data( return (0); } -/* Pause message from server. */ int client_msg_fn_pause( struct hdr *hdr, unused struct client_ctx *cctx, unused char **error) @@ -92,17 +89,6 @@ client_msg_fn_pause( return (1); } -/* Okay message from server. */ -int -client_msg_fn_okay( - struct hdr *hdr, unused struct client_ctx *cctx, unused char **error) -{ - if (hdr->size != 0) - fatalx("bad MSG_OKAY size"); - return (0); -} - -/* Error message from server. */ int client_msg_fn_error(struct hdr *hdr, struct client_ctx *cctx, char **error) { @@ -116,7 +102,6 @@ client_msg_fn_error(struct hdr *hdr, struct client_ctx *cctx, char **error) return (-1); } -/* Detach message from server. */ int client_msg_fn_detach( struct hdr *hdr, unused struct client_ctx *cctx, char **error) diff --git a/client.c b/client.c index 77a1fb68..0c60d540 100644 --- a/client.c +++ b/client.c @@ -1,4 +1,4 @@ -/* $Id: client.c,v 1.10 2007-10-03 10:18:32 nicm Exp $ */ +/* $Id: client.c,v 1.11 2007-10-03 21:31:07 nicm Exp $ */ /* * Copyright (c) 2007 Nicholas Marriott @@ -38,33 +38,19 @@ int client_process_local(struct client_ctx *, char **); int client_init(char *path, struct client_ctx *cctx, int start_server) { - struct sockaddr_un sa; - struct stat sb; - size_t sz; - int mode; - u_int retries; + struct sockaddr_un sa; + struct stat sb; + struct msg_identify_data data; + struct winsize ws; + size_t sz; + int mode; + u_int retries; if (path == NULL) { xasprintf(&path, "%s/%s-%lu", _PATH_TMP, __progname, (u_long) getuid()); } - if (start_server) { - if (!isatty(STDIN_FILENO)) { - log_warnx("stdin is not a tty"); - return (-1); - } - if (!isatty(STDOUT_FILENO)) { - log_warnx("stdout is not a tty"); - return (-1); - } - - if (ioctl(STDIN_FILENO, TIOCGWINSZ, &cctx->ws) == -1) { - log_warn("ioctl(TIOCGWINSZ)"); - return (-1); - } - } - retries = 0; retry: if (stat(path, &sb) != 0) { @@ -121,50 +107,18 @@ retry: cctx->srv_in = buffer_create(BUFSIZ); cctx->srv_out = buffer_create(BUFSIZ); - return (0); -} - -int -client_flush(struct client_ctx *cctx) -{ - struct pollfd pfd; - struct hdr hdr; - - for (;;) { - pfd.fd = cctx->srv_fd; - pfd.events = POLLIN; - if (BUFFER_USED(cctx->srv_out) > 0) - pfd.events |= POLLOUT; - - if (poll(&pfd, 1, INFTIM) == -1) { - if (errno == EAGAIN || errno == EINTR) - continue; - fatal("poll failed"); + if (isatty(STDIN_FILENO) && isatty(STDOUT_FILENO)) { + if (ioctl(STDIN_FILENO, TIOCGWINSZ, &ws) == -1) { + log_warn("ioctl(TIOCGWINSZ)"); + return (-1); } - if (buffer_poll(&pfd, cctx->srv_in, cctx->srv_out) != 0) { - log_warnx("lost server"); - return (1); - } - - if (BUFFER_USED(cctx->srv_in) < sizeof hdr) - continue; - memcpy(&hdr, BUFFER_OUT(cctx->srv_in), sizeof hdr); - if (BUFFER_USED(cctx->srv_in) < (sizeof hdr) + hdr.size) - continue; - buffer_remove(cctx->srv_in, sizeof hdr); - - if (hdr.type == MSG_OKAY) - return (0); - if (hdr.type == MSG_ERROR) { - if (hdr.size > INT_MAX - 1) - fatalx("bad MSG_ERROR size"); - log_warnx( - "%.*s", (int) hdr.size, BUFFER_OUT(cctx->srv_in)); - return (1); - } - fatalx("unexpected message"); + data.sx = ws.ws_col; + data.sy = ws.ws_row; + client_write_server(cctx, MSG_IDENTIFY, &data, sizeof data); } + + return (0); } int @@ -244,14 +198,15 @@ local_dead: void client_handle_winch(struct client_ctx *cctx) { - struct size_data data; - - if (ioctl(STDIN_FILENO, TIOCGWINSZ, &cctx->ws) == -1) + struct msg_resize_data data; + struct winsize ws; + + if (ioctl(STDIN_FILENO, TIOCGWINSZ, &ws) == -1) fatal("ioctl failed"); - data.sx = cctx->ws.ws_col; - data.sy = cctx->ws.ws_row; - client_write_server(cctx, MSG_SIZE, &data, sizeof data); + data.sx = ws.ws_col; + data.sy = ws.ws_row; + client_write_server(cctx, MSG_RESIZE, &data, sizeof data); sigwinch = 0; } diff --git a/cmd-detach-session.c b/cmd-detach-session.c new file mode 100644 index 00000000..bcb858a7 --- /dev/null +++ b/cmd-detach-session.c @@ -0,0 +1,137 @@ +/* $Id: cmd-detach-session.c,v 1.1 2007-10-03 21:31:07 nicm Exp $ */ + +/* + * Copyright (c) 2007 Nicholas Marriott + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF MIND, USE, DATA OR PROFITS, WHETHER + * IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING + * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#include + +#include + +#include "tmux.h" + +/* + * Detach session. If called with -a detach all clients attached to specified + * session, otherwise detach current session on key press only. + */ + +int cmd_detach_session_parse(void **, int, char **, char **); +const char *cmd_detach_session_usage(void); +void cmd_detach_session_exec(void *, struct cmd_ctx *); +void cmd_detach_session_send(void *, struct buffer *); +void cmd_detach_session_recv(void **, struct buffer *); +void cmd_detach_session_free(void *); + +struct cmd_detach_session_data { + int flag_all; +}; + +const struct cmd_entry cmd_detach_session_entry = { + CMD_DETACHSESSION, "detach-session", "detach", 0, + cmd_detach_session_parse, + cmd_detach_session_usage, + cmd_detach_session_exec, + cmd_detach_session_send, + cmd_detach_session_recv, + cmd_detach_session_free +}; + +int +cmd_detach_session_parse(void **ptr, int argc, char **argv, char **cause) +{ + struct cmd_detach_session_data *data; + int opt; + + *ptr = data = xmalloc(sizeof *data); + data->flag_all = 0; + + while ((opt = getopt(argc, argv, "a")) != EOF) { + switch (opt) { + case 'a': + data->flag_all = 1; + break; + default: + goto usage; + } + } + argc -= optind; + argv -= optind; + if (argc != 0) + goto usage; + + return (0); + +usage: + usage(cause, "%s", cmd_detach_session_usage()); + + xfree(data); + return (-1); +} + +const char * +cmd_detach_session_usage(void) +{ + return ("detach-session [-a]"); +} + +void +cmd_detach_session_exec(void *ptr, struct cmd_ctx *ctx) +{ + struct cmd_detach_session_data *data = ptr, std = { 0 }; + struct client *c = ctx->client, *cp; + struct session *s = ctx->session; + u_int i; + + if (data == NULL) + data = &std; + + if (data->flag_all) { + for (i = 0; i < ARRAY_LENGTH(&clients); i++) { + cp = ARRAY_ITEM(&clients, i); + if (cp == NULL || cp->session != s) + continue; + server_write_client(cp, MSG_DETACH, NULL, 0); + } + } else if (ctx->flags & CMD_KEY) + server_write_client(c, MSG_DETACH, NULL, 0); + + if (!(ctx->flags & CMD_KEY)) + server_write_client(c, MSG_EXIT, NULL, 0); +} + +void +cmd_detach_session_send(void *ptr, struct buffer *b) +{ + struct cmd_detach_session_data *data = ptr; + + buffer_write(b, data, sizeof *data); +} + +void +cmd_detach_session_recv(void **ptr, struct buffer *b) +{ + struct cmd_detach_session_data *data; + + *ptr = data = xmalloc(sizeof *data); + buffer_read(b, data, sizeof *data); +} + +void +cmd_detach_session_free(void *ptr) +{ + struct cmd_detach_session_data *data = ptr; + + xfree(data); +} diff --git a/cmd-list-sessions.c b/cmd-list-sessions.c new file mode 100644 index 00000000..47f3f0a0 --- /dev/null +++ b/cmd-list-sessions.c @@ -0,0 +1,70 @@ +/* $Id: cmd-list-sessions.c,v 1.1 2007-10-03 21:31:07 nicm Exp $ */ + +/* + * Copyright (c) 2007 Nicholas Marriott + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF MIND, USE, DATA OR PROFITS, WHETHER + * IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING + * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#include + +#include +#include + +#include "tmux.h" + +/* + * List all sessions. + */ + +int cmd_list_sessions_parse(void **, int, char **, char **); +const char *cmd_list_sessions_usage(void); +void cmd_list_sessions_exec(void *, struct cmd_ctx *); + +const struct cmd_entry cmd_list_sessions_entry = { + CMD_LISTSESSIONS, "list-sessions", "ls", CMD_NOSESSION, + NULL, + NULL, + cmd_list_sessions_exec, + NULL, + NULL, + NULL +}; + +void +cmd_list_sessions_exec(unused void *ptr, struct cmd_ctx *ctx) +{ + struct client *c = ctx->client; + struct session *s = ctx->session; + char *tim; + u_int i, j, n; + + for (i = 0; i < ARRAY_LENGTH(&sessions); i++) { + s = ARRAY_ITEM(&sessions, i); + if (s == NULL) + continue; + + n = 0; + for (j = 0; j < ARRAY_LENGTH(&s->windows); j++) { + if (ARRAY_ITEM(&s->windows, j) != NULL) + n++; + } + tim = ctime(&s->tim); + *strchr(tim, '\n') = '\0'; + + ctx->print(ctx, "%s: %u windows (created %s)", s->name, n, tim); + } + + if (!(ctx->flags & CMD_KEY)) + server_write_client(c, MSG_EXIT, NULL, 0); +} diff --git a/cmd-new-session.c b/cmd-new-session.c new file mode 100644 index 00000000..6ed04ed8 --- /dev/null +++ b/cmd-new-session.c @@ -0,0 +1,162 @@ +/* $Id: cmd-new-session.c,v 1.1 2007-10-03 21:31:07 nicm Exp $ */ + +/* + * Copyright (c) 2007 Nicholas Marriott + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF MIND, USE, DATA OR PROFITS, WHETHER + * IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING + * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#include + +#include + +#include "tmux.h" + +/* + * Create a new session and attach to the current terminal unless -d is given. + */ + +int cmd_new_session_parse(void **, int, char **, char **); +const char *cmd_new_session_usage(void); +void cmd_new_session_exec(void *, struct cmd_ctx *); +void cmd_new_session_send(void *, struct buffer *); +void cmd_new_session_recv(void **, struct buffer *); +void cmd_new_session_free(void *); + +struct cmd_new_session_data { + char *name; + int flag_detached; +}; + +const struct cmd_entry cmd_new_session_entry = { + CMD_NEWSESSION, "new-session", "new", CMD_STARTSERVER|CMD_NOSESSION, + cmd_new_session_parse, + cmd_new_session_usage, + cmd_new_session_exec, + cmd_new_session_send, + cmd_new_session_recv, + cmd_new_session_free +}; + +int +cmd_new_session_parse(void **ptr, int argc, char **argv, char **cause) +{ + struct cmd_new_session_data *data; + int opt; + + *ptr = data = xmalloc(sizeof *data); + data->flag_detached = 0; + data->name = NULL; + + while ((opt = getopt(argc, argv, "ds:")) != EOF) { + switch (opt) { + case 'd': + data->flag_detached = 1; + break; + case 's': + data->name = xstrdup(optarg); + break; + default: + goto usage; + } + } + argc -= optind; + argv -= optind; + if (argc != 0) + goto usage; + + return (0); + +usage: + usage(cause, "%s", cmd_new_session_usage()); + + if (data->name != NULL) + xfree(data->name); + xfree(data); + return (-1); +} + +const char * +cmd_new_session_usage(void) +{ + return ("new-session [-d] [-n session name]"); +} + +void +cmd_new_session_exec(void *ptr, struct cmd_ctx *ctx) +{ + struct cmd_new_session_data *data = ptr, std = { NULL, 0 }; + struct client *c = ctx->client; + u_int sy; + + if (data == NULL) + data = &std; + + if (ctx->flags & CMD_KEY) + return; + + if (!data->flag_detached && !(c->flags & CLIENT_TERMINAL)) { + ctx->error(ctx, "not a terminal"); + return; + } + + if (data->name != NULL && session_find(data->name) != NULL) { + ctx->error(ctx, "duplicate session: %s", data->name); + return; + } + + sy = c->sy; + if (sy < status_lines) + sy = status_lines + 1; + sy -= status_lines; + + c->session = session_create(data->name, default_command, c->sx, sy); + if (c->session == NULL) + fatalx("session_create failed"); + + if (data->flag_detached) + server_write_client(c, MSG_EXIT, NULL, 0); + else { + server_write_client(c, MSG_READY, NULL, 0); + server_draw_client(c); + } +} + +void +cmd_new_session_send(void *ptr, struct buffer *b) +{ + struct cmd_new_session_data *data = ptr; + + buffer_write(b, data, sizeof *data); + cmd_send_string(b, data->name); +} + +void +cmd_new_session_recv(void **ptr, struct buffer *b) +{ + struct cmd_new_session_data *data; + + *ptr = data = xmalloc(sizeof *data); + buffer_read(b, data, sizeof *data); + data->name = cmd_recv_string(b); +} + +void +cmd_new_session_free(void *ptr) +{ + struct cmd_new_session_data *data = ptr; + + if (data->name != NULL) + xfree(data->name); + xfree(data); +} diff --git a/cmd.c b/cmd.c index c5823f3b..cf171de4 100644 --- a/cmd.c +++ b/cmd.c @@ -1,4 +1,4 @@ -/* $Id: cmd.c,v 1.4 2007-10-03 12:43:47 nicm Exp $ */ +/* $Id: cmd.c,v 1.5 2007-10-03 21:31:07 nicm Exp $ */ /* * Copyright (c) 2007 Nicholas Marriott @@ -18,243 +18,163 @@ #include -#include +#include #include #include "tmux.h" -int cmd_prefix = META; - -void cmd_fn_detach(struct client *, struct cmd *); -void cmd_fn_last(struct client *, struct cmd *); -void cmd_fn_meta(struct client *, struct cmd *); -void cmd_fn_new(struct client *, struct cmd *); -void cmd_fn_next(struct client *, struct cmd *); -void cmd_fn_previous(struct client *, struct cmd *); -void cmd_fn_refresh(struct client *, struct cmd *); -void cmd_fn_select(struct client *, struct cmd *); -void cmd_fn_info(struct client *, struct cmd *); - -const struct cmd cmd_default[] = { - { '0', cmd_fn_select, 0, NULL }, - { '1', cmd_fn_select, 1, NULL }, - { '2', cmd_fn_select, 2, NULL }, - { '3', cmd_fn_select, 3, NULL }, - { '4', cmd_fn_select, 4, NULL }, - { '5', cmd_fn_select, 5, NULL }, - { '6', cmd_fn_select, 6, NULL }, - { '7', cmd_fn_select, 7, NULL }, - { '8', cmd_fn_select, 8, NULL }, - { '9', cmd_fn_select, 9, NULL }, - { 'C', cmd_fn_new, 0, NULL }, - { 'c', cmd_fn_new, 0, NULL }, - { 'D', cmd_fn_detach, 0, NULL }, - { 'd', cmd_fn_detach, 0, NULL }, - { 'N', cmd_fn_next, 0, NULL }, - { 'n', cmd_fn_next, 0, NULL }, - { 'P', cmd_fn_previous, 0, NULL }, - { 'p', cmd_fn_previous, 0, NULL }, - { 'R', cmd_fn_refresh, 0, NULL }, - { 'r', cmd_fn_refresh, 0, NULL }, - { 'L', cmd_fn_last, 0, NULL }, - { 'l', cmd_fn_last, 0, NULL }, - { 'I', cmd_fn_info, 0, NULL }, - { 'i', cmd_fn_info, 0, NULL }, - { META, cmd_fn_meta, 0, NULL }, +const struct cmd_entry *cmd_table[] = { + &cmd_detach_session_entry, + &cmd_list_sessions_entry, + &cmd_new_session_entry, + NULL }; -u_int cmd_count = (sizeof cmd_default / sizeof cmd_default[0]); -struct cmd *cmd_table; -const struct bind cmd_bind_table[] = { - { "detach", cmd_fn_detach, 0 }, - { "info", cmd_fn_info, 0 }, - { "last", cmd_fn_last, 0 }, - { "meta", cmd_fn_meta, 0 }, - { "new", cmd_fn_new, BIND_STRING|BIND_USER }, - { "next", cmd_fn_next, 0 }, - { "previous", cmd_fn_previous, 0 }, - { "refresh", cmd_fn_refresh, 0 }, - { "select", cmd_fn_select, BIND_NUMBER|BIND_USER }, -}; -#define NCMDBIND (sizeof cmd_bind_table / sizeof cmd_bind_table[0]) - -const struct bind * -cmd_lookup_bind(const char *name) +struct cmd * +cmd_parse(int argc, char **argv, char **cause) { - const struct bind *bind; - u_int i; + const struct cmd_entry **this, *entry; + struct cmd *cmd; + int opt; - for (i = 0; i < NCMDBIND; i++) { - bind = cmd_bind_table + i; - if (strcmp(bind->name, name) == 0) - return (bind); + *cause = NULL; + if (argc == 0) + return (NULL); + + entry = NULL; + for (this = cmd_table; *this != NULL; this++) { + if (strcmp((*this)->alias, argv[0]) == 0) { + entry = *this; + break; + } + + if (strncmp((*this)->name, argv[0], strlen(argv[0])) != 0) + continue; + if (entry != NULL) { + xasprintf(cause, "ambiguous command: %s", argv[0]); + return (NULL); + } + entry = *this; } + if (entry == NULL) { + xasprintf(cause, "unknown command: %s", argv[0]); + return (NULL); + } + + optind = 1; + if (entry->parse == NULL) { + while ((opt = getopt(argc, argv, "")) != EOF) { + switch (opt) { + default: + goto usage; + } + } + argc -= optind; + argv += optind; + if (argc != 0) + goto usage; + } + + cmd = xmalloc(sizeof *cmd); + cmd->entry = entry; + if (entry->parse != NULL) { + if (entry->parse(&cmd->data, argc, argv, cause) != 0) { + xfree(cmd); + return (NULL); + } + } + return (cmd); + +usage: + if (entry->usage == NULL) + usage(cause, "%s", entry->name); + else + usage(cause, "%s", entry->usage()); return (NULL); } void -cmd_add_bind(int key, u_int num, char *str, const struct bind *bind) +cmd_exec(struct cmd *cmd, struct cmd_ctx *ctx) { - struct cmd *cmd = NULL; - u_int i; - - for (i = 0; i < cmd_count; i++) { - cmd = cmd_table + i; - if (cmd->key == key) - break; - } - if (i == cmd_count) { - for (i = 0; i < cmd_count; i++) { - cmd = cmd_table + i; - if (cmd->key == KEYC_NONE) - break; - } - if (i == cmd_count) { - cmd_count++; - cmd_table = xrealloc(cmd_table, - cmd_count, sizeof cmd_table[0]); - cmd = cmd_table + cmd_count - 1; - } - } - - cmd->key = key; - cmd->fn = bind->fn; - if (bind->flags & BIND_USER) { - if (bind->flags & BIND_STRING) - cmd->str = xstrdup(str); - if (bind->flags & BIND_NUMBER) - cmd->num = num; - } + return (cmd->entry->exec(cmd->data, ctx)); } void -cmd_remove_bind(int key) +cmd_send(struct cmd *cmd, struct buffer *b) { - struct cmd *cmd; - u_int i; + buffer_write(b, &cmd->entry->type, sizeof cmd->entry->type); - for (i = 0; i < cmd_count; i++) { - cmd = cmd_table + i; - if (cmd->key == key) { - cmd->key = KEYC_NONE; + if (cmd->entry->send == NULL) + return; + return (cmd->entry->send(cmd->data, b)); +} + +struct cmd * +cmd_recv(struct buffer *b) +{ + const struct cmd_entry **this, *entry; + struct cmd *cmd; + enum cmd_type type; + + buffer_read(b, &type, sizeof type); + + entry = NULL; + for (this = cmd_table; *this != NULL; this++) { + if ((*this)->type == type) { + entry = *this; break; } } + if (*this == NULL) + return (NULL); + + cmd = xmalloc(sizeof *cmd); + cmd->entry = entry; + + if (cmd->entry->recv != NULL) + cmd->entry->recv(&cmd->data, b); + return (cmd); } void -cmd_init(void) +cmd_free(struct cmd *cmd) { - cmd_table = xmalloc(sizeof cmd_default); - memcpy(cmd_table, cmd_default, sizeof cmd_default); + if (cmd->entry->free != NULL) + cmd->entry->free(cmd->data); + xfree(cmd); } void -cmd_free(void) +cmd_send_string(struct buffer *b, const char *s) { - /* XXX free strings */ - xfree(cmd_table); -} - -void -cmd_dispatch(struct client *c, int key) -{ - struct cmd *cmd; - u_int i; - - for (i = 0; i < cmd_count; i++) { - cmd = cmd_table + i; - if (cmd->key != KEYC_NONE && cmd->key == key) - cmd->fn(c, cmd); + size_t n; + + if (s == NULL) { + n = 0; + buffer_write(b, &n, sizeof n); + return; } + + n = strlen(s) + 1; + buffer_write(b, &n, sizeof n); + + buffer_write(b, s, n); } -void -cmd_fn_new(struct client *c, struct cmd *cmd) +char * +cmd_recv_string(struct buffer *b) { - char *s; + char *s; + size_t n; - s = cmd->str; - if (s == NULL) - s = default_command; - if (session_new(c->session, s, c->sx, c->sy) != 0) - server_write_message(c, "%s failed", s); /* XXX */ - else - server_draw_client(c, 0, c->sy - 1); -} - -void -cmd_fn_detach(struct client *c, unused struct cmd *cmd) -{ - server_write_client(c, MSG_DETACH, NULL, 0); -} - -void -cmd_fn_last(struct client *c, unused struct cmd *cmd) -{ - if (session_last(c->session) == 0) - server_window_changed(c); - else - server_write_message(c, "No last window"); -} - -void -cmd_fn_meta(struct client *c, unused struct cmd *cmd) -{ - window_key(c->session->window, cmd_prefix); -} - -void -cmd_fn_next(struct client *c, unused struct cmd *cmd) -{ - if (session_next(c->session) == 0) - server_window_changed(c); - else - server_write_message(c, "No next window"); -} - -void -cmd_fn_previous(struct client *c, unused struct cmd *cmd) -{ - if (session_previous(c->session) == 0) - server_window_changed(c); - else - server_write_message(c, "No previous window"); -} - -void -cmd_fn_refresh(struct client *c, unused struct cmd *cmd) -{ - server_draw_client(c, 0, c->sy - 1); -} - -void -cmd_fn_select(struct client *c, struct cmd *cmd) -{ - if (session_select(c->session, cmd->num) == 0) - server_window_changed(c); - else - server_write_message(c, "Window %u not present", cmd->num); -} - -void -cmd_fn_info(struct client *c, unused struct cmd *cmd) -{ - struct window *w; - char *buf; - size_t len; - u_int i; - - len = c->sx + 1; - buf = xmalloc(len); - - w = c->session->window; - window_index(&c->session->windows, w, &i); - xsnprintf(buf, len, "%u:%s \"%s\" (size %u,%u) (cursor %u,%u) " - "(region %u,%u)", i, w->name, w->screen.title, w->screen.sx, - w->screen.sy, w->screen.cx, w->screen.cy, w->screen.ry_upper, - w->screen.ry_lower); - - server_write_message(c, "%s", buf); - xfree(buf); + buffer_read(b, &n, sizeof n); + + if (n == 0) + return (NULL); + + s = xmalloc(n); + buffer_read(b, s, n); + s[n - 1] = '\0'; + + return (s); } diff --git a/key-bindings.c b/key-bindings.c new file mode 100644 index 00000000..7cbd556b --- /dev/null +++ b/key-bindings.c @@ -0,0 +1,214 @@ +/* $Id: key-bindings.c,v 1.1 2007-10-03 21:31:07 nicm Exp $ */ + +/* + * Copyright (c) 2007 Nicholas Marriott + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF MIND, USE, DATA OR PROFITS, WHETHER + * IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING + * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#include + +#include +#include + +#include "tmux.h" + +ARRAY_DECL(, struct binding *) key_bindings; + +void key_bindings_error(struct cmd_ctx *, const char *, ...); +void key_bindings_print(struct cmd_ctx *, const char *, ...); + +void +key_bindings_add(int key, struct cmd *cmd) +{ + struct binding *bd; + u_int i; + + bd = NULL; + for (i = 0; i < ARRAY_LENGTH(&key_bindings); i++) { + bd = ARRAY_ITEM(&key_bindings, i); + if (bd->key == key) + break; + } + if (i == ARRAY_LENGTH(&key_bindings)) { + bd = xmalloc(sizeof *bd); + ARRAY_ADD(&key_bindings, bd); + } + + bd->key = key; + bd->cmd = cmd; +} + +void +key_bindings_remove(int key) +{ + struct binding *bd; + u_int i; + + bd = NULL; + for (i = 0; i < ARRAY_LENGTH(&key_bindings); i++) { + bd = ARRAY_ITEM(&key_bindings, i); + if (bd->key == key) + break; + } + if (i == ARRAY_LENGTH(&key_bindings)) + return; + + cmd_free(bd->cmd); + xfree(bd); +} + +void +key_bindings_init(void) +{ + struct { + int key; + const struct cmd_entry *entry; + } table[] = { + { 'D', &cmd_detach_session_entry }, + { 'd', &cmd_detach_session_entry }, + { 'S', &cmd_list_sessions_entry }, + { 's', &cmd_list_sessions_entry }, +/* { 'C', &cmd_new_window }, + { 'c', &cmd_new_window }, + { 'N', &cmd_next_window }, + { 'n', &cmd_next_window }, + { 'P', &cmd_previous_window }, + { 'p', &cmd_previous_window }, + { 'R', &cmd_refresh_client }, + { 'r', &cmd_refresh_client }, + { 'L', &cmd_last_window }, + { 'l', &cmd_last_window }, + { 'I', &cmd_windo_info }, + { 'i', &cmd_window_info }, + { META, &cmd_meta_entry }, +*//* { '0', cmdx_fn_select, 0, NULL }, + { '1', cmdx_fn_select, 1, NULL }, + { '2', cmdx_fn_select, 2, NULL }, + { '3', cmdx_fn_select, 3, NULL }, + { '4', cmdx_fn_select, 4, NULL }, + { '5', cmdx_fn_select, 5, NULL }, + { '6', cmdx_fn_select, 6, NULL }, + { '7', cmdx_fn_select, 7, NULL }, + { '8', cmdx_fn_select, 8, NULL }, + { '9', cmdx_fn_select, 9, NULL }, +*/ + }; + u_int i; + struct cmd *cmd; + + for (i = 0; i < (sizeof table / sizeof table[0]); i++) { + cmd = xmalloc(sizeof *cmd); + cmd->entry = table[i].entry; + cmd->data = NULL; + key_bindings_add(table[i].key, cmd); + } +} + +void +key_bindings_free(void) +{ + struct binding *bd; + u_int i; + + for (i = 0; i < ARRAY_LENGTH(&key_bindings); i++) { + bd = ARRAY_ITEM(&key_bindings, i); + + cmd_free(bd->cmd); + xfree(bd); + } + + ARRAY_FREEALL(&key_bindings); +} + +void +key_bindings_error(struct cmd_ctx *ctx, const char *fmt, ...) +{ + va_list ap; + char *msg; + + va_start(ap, fmt); + xvasprintf(&msg, fmt, ap); + va_end(ap); + + server_write_message(ctx->client, msg); + xfree(msg); +} + +void +key_bindings_print(struct cmd_ctx *ctx, const char *fmt, ...) +{ + struct client *c = ctx->client; + struct hdr hdr; + va_list ap; + char *msg; + size_t size; + u_int i; + + buffer_ensure(c->out, sizeof hdr); + buffer_add(c->out, sizeof hdr); + size = BUFFER_USED(c->out); + + if (!(c->flags & CLIENT_HOLD)) { + input_store_zero(c->out, CODE_CURSOROFF); + for (i = 0; i < c->session->window->screen.sy; i++) { + input_store_two(c->out, CODE_CURSORMOVE, i + 1, 1); + input_store_zero(c->out, CODE_CLEARLINE); + } + input_store_two(c->out, CODE_CURSORMOVE, 1, 1); + input_store_two(c->out, CODE_ATTRIBUTES, 0, 0x88); + + c->flags |= CLIENT_HOLD; + } + + va_start(ap, fmt); + xvasprintf(&msg, fmt, ap); + va_end(ap); + + buffer_write(c->out, msg, strlen(msg)); + input_store8(c->out, '\r'); + input_store8(c->out, '\n'); + xfree(msg); + + size = BUFFER_USED(c->out) - size; + hdr.type = MSG_DATA; + hdr.size = size; + memcpy(BUFFER_IN(c->out) - size - sizeof hdr, &hdr, sizeof hdr); +} + +void +key_bindings_dispatch(int key, struct client *c) +{ + struct cmd_ctx ctx; + struct binding *bd; + u_int i; + + bd = NULL; + for (i = 0; i < ARRAY_LENGTH(&key_bindings); i++) { + bd = ARRAY_ITEM(&key_bindings, i); + if (bd->key == key) + break; + } + if (i == ARRAY_LENGTH(&key_bindings)) + return; + + ctx.session = c->session; + + ctx.error = key_bindings_error; + ctx.print = key_bindings_print; + + ctx.client = c; + ctx.flags = CMD_KEY; + + cmd_exec(bd->cmd, &ctx); +} diff --git a/op-list.c b/op-list.c deleted file mode 100644 index a8405d7c..00000000 --- a/op-list.c +++ /dev/null @@ -1,199 +0,0 @@ -/* $Id: op-list.c,v 1.6 2007-09-29 15:06:00 nicm Exp $ */ - -/* - * Copyright (c) 2007 Nicholas Marriott - * - * Permission to use, copy, modify, and distribute this software for any - * purpose with or without fee is hereby granted, provided that the above - * copyright notice and this permission notice appear in all copies. - * - * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES - * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF - * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR - * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES - * WHATSOEVER RESULTING FROM LOSS OF MIND, USE, DATA OR PROFITS, WHETHER - * IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING - * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. - */ - -#include - -#include -#include -#include - -#include "tmux.h" - -int -op_list_sessions(char *path, int argc, unused char **argv) -{ - struct client_ctx cctx; - char *tim; - struct sessions_data data; - struct sessions_entry ent; - struct pollfd pfd; - struct hdr hdr; - - if (argc != 1) - return (usage("list-sessions")); - - if (client_init(path, &cctx, 0) != 0) - return (1); - client_write_server(&cctx, MSG_SESSIONS, &data, sizeof data); - - for (;;) { - pfd.fd = cctx.srv_fd; - pfd.events = POLLIN; - if (BUFFER_USED(cctx.srv_out) > 0) - pfd.events |= POLLOUT; - - if (poll(&pfd, 1, INFTIM) == -1) { - if (errno == EAGAIN || errno == EINTR) - continue; - log_warn("poll"); - return (-1); - } - - if (buffer_poll(&pfd, cctx.srv_in, cctx.srv_out) != 0) { - log_warnx("lost server"); - return (-1); - } - - if (BUFFER_USED(cctx.srv_in) < sizeof hdr) - continue; - memcpy(&hdr, BUFFER_OUT(cctx.srv_in), sizeof hdr); - if (BUFFER_USED(cctx.srv_in) < (sizeof hdr) + hdr.size) - continue; - buffer_remove(cctx.srv_in, sizeof hdr); - - if (hdr.type == MSG_ERROR) { - if (hdr.size > INT_MAX - 1) - fatalx("bad MSG_ERROR size"); - log_warnx( - "%.*s", (int) hdr.size, BUFFER_OUT(cctx.srv_in)); - return (1); - } - if (hdr.type != MSG_SESSIONS) - fatalx("unexpected message"); - - if (hdr.size < sizeof data) - fatalx("bad MSG_SESSIONS size"); - buffer_read(cctx.srv_in, &data, sizeof data); - hdr.size -= sizeof data; - if (data.sessions == 0 && hdr.size == 0) - return (0); - if (hdr.size < data.sessions * sizeof ent) - fatalx("bad MSG_SESSIONS size"); - - while (data.sessions-- > 0) { - buffer_read(cctx.srv_in, &ent, sizeof ent); - - tim = ctime(&ent.tim); - *strchr(tim, '\n') = '\0'; - - printf("%s: %u windows " - "(created %s)\n", ent.name, ent.windows, tim); - } - - return (0); - } -} - -int -op_list_windows(char *path, int argc, char **argv) -{ - struct client_ctx cctx; - char name[MAXNAMELEN]; - int opt; - struct windows_data data; - struct windows_entry ent; - struct pollfd pfd; - struct hdr hdr; - - *name = '\0'; - optind = 1; - while ((opt = getopt(argc, argv, "s:?")) != EOF) { - switch (opt) { - case 's': - if (strlcpy(name, optarg, sizeof name) >= sizeof name) { - log_warnx("%s: session name too long", optarg); - return (1); - } - break; - case '?': - default: - return (usage("list-windows [-s session]")); - } - } - argc -= optind; - argv += optind; - if (argc != 0) - return (usage("list-windows [-s session]")); - - if (client_init(path, &cctx, 0) != 0) - return (1); - - client_fill_sessid(&data.sid, name); - client_write_server(&cctx, MSG_WINDOWS, &data, sizeof data); - - for (;;) { - pfd.fd = cctx.srv_fd; - pfd.events = POLLIN; - if (BUFFER_USED(cctx.srv_out) > 0) - pfd.events |= POLLOUT; - - if (poll(&pfd, 1, INFTIM) == -1) { - if (errno == EAGAIN || errno == EINTR) - continue; - log_warn("poll"); - return (-1); - } - - if (buffer_poll(&pfd, cctx.srv_in, cctx.srv_out) != 0) { - log_warnx("lost server"); - return (-1); - } - - if (BUFFER_USED(cctx.srv_in) < sizeof hdr) - continue; - memcpy(&hdr, BUFFER_OUT(cctx.srv_in), sizeof hdr); - if (BUFFER_USED(cctx.srv_in) < (sizeof hdr) + hdr.size) - continue; - buffer_remove(cctx.srv_in, sizeof hdr); - - if (hdr.type == MSG_ERROR) { - if (hdr.size > INT_MAX - 1) - fatalx("bad MSG_ERROR size"); - log_warnx( - "%.*s", (int) hdr.size, BUFFER_OUT(cctx.srv_in)); - return (1); - } - if (hdr.type != MSG_WINDOWS) - fatalx("unexpected message"); - - if (hdr.size < sizeof data) - fatalx("bad MSG_WINDOWS size"); - buffer_read(cctx.srv_in, &data, sizeof data); - hdr.size -= sizeof data; - if (data.windows == 0 && hdr.size == 0) { - log_warnx("session not found: %s", name); - return (1); - } - if (hdr.size < data.windows * sizeof ent) - fatalx("bad MSG_WINDOWS size"); - - while (data.windows-- > 0) { - buffer_read(cctx.srv_in, &ent, sizeof ent); - - if (*ent.title != '\0') { - printf("%u: %s \"%s\" (%s)\n", ent.idx, - ent.name, ent.title, ent.tty); - } else { - printf("%u: %s (%s)\n", - ent.idx, ent.name, ent.tty); - } - } - - return (0); - } -} diff --git a/op.c b/op.c deleted file mode 100644 index 80ab66a6..00000000 --- a/op.c +++ /dev/null @@ -1,271 +0,0 @@ -/* $Id: op.c,v 1.12 2007-10-03 12:43:47 nicm Exp $ */ - -/* - * Copyright (c) 2007 Nicholas Marriott - * - * Permission to use, copy, modify, and distribute this software for any - * purpose with or without fee is hereby granted, provided that the above - * copyright notice and this permission notice appear in all copies. - * - * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES - * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF - * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR - * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES - * WHATSOEVER RESULTING FROM LOSS OF MIND, USE, DATA OR PROFITS, WHETHER - * IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING - * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. - */ - -#include - -#include -#include -#include - -#include "tmux.h" - -int -op_new_session(char *path, int argc, char **argv) -{ - struct new_data data; - struct client_ctx cctx; - char name[MAXNAMELEN]; - int opt, detached; - - *name = '\0'; - detached = 0; - optind = 1; - while ((opt = getopt(argc, argv, "s:d?")) != EOF) { - switch (opt) { - case 's': - if (strlcpy(name, optarg, sizeof name) >= sizeof name) { - log_warnx("session name too long: %s", optarg); - return (1); - } - break; - case 'd': - detached = 1; - break; - case '?': - default: - return (usage("new-session [-d] [-s session]")); - } - } - argc -= optind; - argv += optind; - if (argc != 0) - return (usage("new-session [-s session]")); - - if (client_init(path, &cctx, 1) != 0) - return (1); - - strlcpy(data.name, name, sizeof data.name); - data.sx = cctx.ws.ws_col; - data.sy = cctx.ws.ws_row; - client_write_server(&cctx, MSG_NEW, &data, sizeof data); - - if (detached) - return (client_flush(&cctx)); - return (client_main(&cctx)); -} - -int -op_attach(char *path, int argc, char **argv) -{ - struct attach_data data; - struct client_ctx cctx; - char name[MAXNAMELEN]; - int opt; - - *name = '\0'; - optind = 1; - while ((opt = getopt(argc, argv, "s:?")) != EOF) { - switch (opt) { - case 's': - if (strlcpy(name, optarg, sizeof name) >= sizeof name) { - log_warnx("session name too long: %s", optarg); - return (1); - } - break; - case '?': - default: - return (usage("attach [-s session]")); - } - } - argc -= optind; - argv += optind; - if (argc != 0) - return (usage("attach [-s session]")); - - if (client_init(path, &cctx, 1) != 0) - return (1); - - client_fill_sessid(&data.sid, name); - data.sx = cctx.ws.ws_col; - data.sy = cctx.ws.ws_row; - client_write_server(&cctx, MSG_ATTACH, &data, sizeof data); - - return (client_main(&cctx)); -} - -int -op_rename_window(char *path, int argc, char **argv) -{ - struct rename_data data; - struct client_ctx cctx; - char sname[MAXNAMELEN]; - int opt; - const char *errstr; - - *sname = '\0'; - data.idx = -1; - optind = 1; - while ((opt = getopt(argc, argv, "i:s:?")) != EOF) { - switch (opt) { - case 's': - if (strlcpy(sname, optarg, sizeof sname) - >= sizeof sname) { - log_warnx("session name too long: %s", optarg); - return (1); - } - break; - case 'i': - data.idx = strtonum(optarg, 0, INT_MAX, &errstr); - if (errstr != NULL) { - log_warnx( - "window index %s: %s", errstr, optarg); - return (1); - } - break; - case '?': - default: - return (usage( - "rename-window [-s session] [-i index] name")); - } - } - argc -= optind; - argv += optind; - if (argc != 1) - return (usage("rename-window [-s session] [-i index] name")); - - if (client_init(path, &cctx, 1) != 0) - return (1); - - client_fill_sessid(&data.sid, sname); - if ((strlcpy(data.newname, argv[0], sizeof data.newname) - >= sizeof data.newname)) { - log_warnx("new window name too long: %s", argv[0]); - return (1); - } - client_write_server(&cctx, MSG_RENAME, &data, sizeof data); - - return (client_flush(&cctx)); -} - -int -op_bind_key(char *path, int argc, char **argv) -{ - struct bind_data data; - struct client_ctx cctx; - int opt; - const char *errstr; - char *str; - size_t len; - const struct bind *bind; - - optind = 1; - while ((opt = getopt(argc, argv, "?")) != EOF) { - switch (opt) { - default: - return (usage("bind-key key command [argument]")); - } - } - argc -= optind; - argv += optind; - if (argc != 2 && argc != 3) - return (usage("bind-key key command [argument]")); - - if ((data.key = key_string_lookup(argv[0])) == KEYC_NONE) { - log_warnx("unknown key: %s", argv[0]); - return (1); - } - if (strlcpy(data.cmd, argv[1], sizeof data.cmd) >= sizeof data.cmd) { - log_warnx("command too long: %s", argv[1]); - return (1); - } - - if ((bind = cmd_lookup_bind(data.cmd)) == NULL) { - log_warnx("unknown command: %s", data.cmd); - return (1); - } - - str = NULL; - len = 0; - if (bind->flags & BIND_USER) { - if (argc != 3) { - log_warnx("%s requires an argument", data.cmd); - return (1); - } - - data.flags |= BIND_USER; - if (bind->flags & BIND_STRING) { - data.flags |= BIND_STRING; - str = argv[2]; - len = strlen(str); - } else if (bind->flags & BIND_NUMBER) { - data.flags |= BIND_NUMBER; - data.num = strtonum(argv[2], 0, UINT_MAX, &errstr); - if (errstr != NULL) { - log_warnx("argument %s: %s", errstr, argv[2]); - return (1); - } - } else - fatalx("no argument type"); - } else { - if (argc != 2) { - log_warnx("%s cannot have an argument", data.cmd); - return (1); - } - - data.flags = 0; - } - - if (client_init(path, &cctx, 1) != 0) - return (1); - - client_write_server2(&cctx, MSG_BINDKEY, &data, sizeof data, str, len); - - return (client_flush(&cctx)); -} - -int -op_unbind_key(char *path, int argc, char **argv) -{ - struct bind_data data; - struct client_ctx cctx; - int opt; - - optind = 1; - while ((opt = getopt(argc, argv, "?")) != EOF) { - switch (opt) { - default: - return (usage("unbind-key key")); - } - } - argc -= optind; - argv += optind; - if (argc != 1) - return (usage("unbind-key key")); - - if ((data.key = key_string_lookup(argv[0])) == KEYC_NONE) { - log_warnx("unknown key: %s", argv[0]); - return (1); - } - - if (client_init(path, &cctx, 1) != 0) - return (1); - - client_write_server(&cctx, MSG_UNBINDKEY, &data, sizeof data); - - return (client_flush(&cctx)); -} diff --git a/server-fn.c b/server-fn.c index f46a4715..7af77146 100644 --- a/server-fn.c +++ b/server-fn.c @@ -1,4 +1,4 @@ -/* $Id: server-fn.c,v 1.13 2007-10-03 12:34:16 nicm Exp $ */ +/* $Id: server-fn.c,v 1.14 2007-10-03 21:31:07 nicm Exp $ */ /* * Copyright (c) 2007 Nicholas Marriott @@ -64,7 +64,7 @@ server_find_sessid(struct sessid *sid, char **cause) } } if (s == NULL) { - xasprintf(cause, "no sessions"); + xasprintf(cause, "no sessions found"); return (NULL); } if (n != 1) { @@ -125,6 +125,8 @@ server_write_clients( for (i = 0; i < ARRAY_LENGTH(&clients); i++) { c = ARRAY_ITEM(&clients, i); if (c != NULL && c->session != NULL) { + if (c->flags & CLIENT_HOLD) /* XXX OUTPUT only */ + continue; if (c->session->window == w) { log_debug( "writing %d to clients: %d", type, c->fd); @@ -145,21 +147,22 @@ server_window_changed(struct client *c) w = c->session->window; if (c->sx != w->screen.sx || c->sy != w->screen.sy) window_resize(w, c->sx, c->sy); - server_draw_client(c, 0, c->sy - 1); + server_draw_client(c); } /* Draw window on client. */ void -server_draw_client(struct client *c, u_int py_upper, u_int py_lower) +server_draw_client(struct client *c) { - struct hdr hdr; - size_t size; + struct hdr hdr; + size_t size; + struct screen *s = &c->session->window->screen; buffer_ensure(c->out, sizeof hdr); buffer_add(c->out, sizeof hdr); size = BUFFER_USED(c->out); - screen_draw(&c->session->window->screen, c->out, py_upper, py_lower); + screen_draw(s, c->out, 0, s->sy - 1); size = BUFFER_USED(c->out) - size; log_debug("redrawing screen, %zu bytes", size); diff --git a/server-msg.c b/server-msg.c index 6de3d34c..89d57b0f 100644 --- a/server-msg.c +++ b/server-msg.c @@ -1,4 +1,4 @@ -/* $Id: server-msg.c,v 1.19 2007-10-03 13:07:42 nicm Exp $ */ +/* $Id: server-msg.c,v 1.20 2007-10-03 21:31:07 nicm Exp $ */ /* * Copyright (c) 2007 Nicholas Marriott @@ -24,16 +24,13 @@ #include "tmux.h" -int server_msg_fn_attach(struct hdr *, struct client *); -int server_msg_fn_bindkey(struct hdr *, struct client *); +int server_msg_fn_command(struct hdr *, struct client *); +int server_msg_fn_identify(struct hdr *, struct client *); int server_msg_fn_keys(struct hdr *, struct client *); -int server_msg_fn_new(struct hdr *, struct client *); -int server_msg_fn_rename(struct hdr *, struct client *); -int server_msg_fn_sessions(struct hdr *, struct client *); -int server_msg_fn_size(struct hdr *, struct client *); -int server_msg_fn_unbindkey(struct hdr *, struct client *); -int server_msg_fn_windowlist(struct hdr *, struct client *); -int server_msg_fn_windows(struct hdr *, struct client *); +int server_msg_fn_resize(struct hdr *, struct client *); + +void server_msg_fn_command_error(struct cmd_ctx *, const char *, ...); +void server_msg_fn_command_print(struct cmd_ctx *, const char *, ...); struct server_msg { enum hdrtype type; @@ -41,16 +38,10 @@ struct server_msg { int (*fn)(struct hdr *, struct client *); }; const struct server_msg server_msg_table[] = { - { MSG_ATTACH, server_msg_fn_attach }, - { MSG_BINDKEY, server_msg_fn_bindkey }, + { MSG_IDENTIFY, server_msg_fn_identify }, + { MSG_COMMAND, server_msg_fn_command }, + { MSG_RESIZE, server_msg_fn_resize }, { MSG_KEYS, server_msg_fn_keys }, - { MSG_NEW, server_msg_fn_new }, - { MSG_RENAME, server_msg_fn_rename }, - { MSG_SESSIONS, server_msg_fn_sessions }, - { MSG_SIZE, server_msg_fn_size }, - { MSG_UNBINDKEY, server_msg_fn_unbindkey }, - { MSG_WINDOWLIST, server_msg_fn_windowlist }, - { MSG_WINDOWS, server_msg_fn_windows }, }; #define NSERVERMSG (sizeof server_msg_table / sizeof server_msg_table[0]) @@ -83,89 +74,98 @@ server_msg_dispatch(struct client *c) } } -/* New message from client. */ -int -server_msg_fn_new(struct hdr *hdr, struct client *c) +void +server_msg_fn_command_error(struct cmd_ctx *ctx, const char *fmt, ...) { - struct new_data data; - char *msg; - - if (c->session != NULL) - return (0); - if (hdr->size != sizeof data) - fatalx("bad MSG_NEW size"); + va_list ap; + char *msg; + + va_start(ap, fmt); + xvasprintf(&msg, fmt, ap); + va_end(ap); + + server_write_client(ctx->client, MSG_ERROR, msg, strlen(msg)); + xfree(msg); +} + +void +server_msg_fn_command_print(struct cmd_ctx *ctx, const char *fmt, ...) +{ + va_list ap; + char *msg; + + va_start(ap, fmt); + xvasprintf(&msg, fmt, ap); + va_end(ap); + + server_write_client(ctx->client, MSG_PRINT, msg, strlen(msg)); + xfree(msg); +} + +int +server_msg_fn_command(struct hdr *hdr, struct client *c) +{ + struct msg_command_data data; + struct cmd_ctx ctx; + struct cmd *cmd; + char *cause; + + if (hdr->size < sizeof data) + fatalx("bad MSG_COMMAND size"); buffer_read(c->in, &data, sizeof data); - c->sx = data.sx; - if (c->sx == 0) - c->sx = 80; - c->sy = data.sy; - if (c->sy == 0) - c->sy = 25; + cmd = cmd_recv(c->in); + log_debug("got command %u %s from client %d", + cmd->entry->type, cmd->entry->name, c->fd); - if (c->sy >= status_lines) - c->sy -= status_lines; + if (cmd->entry->flags & CMD_NOSESSION) + ctx.session = NULL; + else { + ctx.session = server_find_sessid(&data.sid, &cause); + if (ctx.session == NULL) { + server_write_error(c, "%s", cause); + xfree(cause); + return (0); + } + } - data.name[(sizeof data.name) - 1] = '\0'; - if (*data.name != '\0' && session_find(data.name) != NULL) { - xasprintf(&msg, "duplicate session: %s", data.name); - server_write_client(c, MSG_ERROR, msg, strlen(msg)); - xfree(msg); - return (0); - } + ctx.error = server_msg_fn_command_error; + ctx.print = server_msg_fn_command_print; - c->session = session_create(data.name, default_command, c->sx, c->sy); - if (c->session == NULL) - fatalx("session_create failed"); + ctx.client = c; + ctx.flags = 0; - server_write_client(c, MSG_OKAY, NULL, 0); - server_draw_client(c, 0, c->sy - 1); + cmd_exec(cmd, &ctx); + cmd_free(cmd); return (0); } -/* Attach message from client. */ int -server_msg_fn_attach(struct hdr *hdr, struct client *c) +server_msg_fn_identify(struct hdr *hdr, struct client *c) { - struct attach_data data; - char *cause; - - if (c->session != NULL) - return (0); - if (hdr->size != sizeof data) - fatalx("bad MSG_ATTACH size"); + struct msg_identify_data data; + + if (hdr->size < sizeof data) + fatalx("bad MSG_IDENTIFY size"); buffer_read(c->in, &data, sizeof data); + log_debug("got identify msg from client: %u,%u", data.sx, data.sy); + c->sx = data.sx; - if (c->sx == 0) - c->sx = 80; c->sy = data.sy; - if (c->sy == 0) - c->sy = 25; - if (c->sy >= status_lines) - c->sy -= status_lines; - - if ((c->session = server_find_sessid(&data.sid, &cause)) == NULL) { - server_write_error(c, "%s", cause); - xfree(cause); - return (0); - } - - server_draw_client(c, 0, c->sy - 1); + c->flags |= CLIENT_TERMINAL; return (0); } -/* Size message from client. */ int -server_msg_fn_size(struct hdr *hdr, struct client *c) +server_msg_fn_resize(struct hdr *hdr, struct client *c) { - struct size_data data; + struct msg_resize_data data; + u_int sy; - if (c->session == NULL) - return (0); if (hdr->size != sizeof data) fatalx("bad MSG_SIZE size"); buffer_read(c->in, &data, sizeof data); @@ -177,265 +177,47 @@ server_msg_fn_size(struct hdr *hdr, struct client *c) if (c->sy == 0) c->sy = 25; - if (c->sy >= status_lines) - c->sy -= status_lines; + sy = c->sy; + if (sy < status_lines) + sy = status_lines + 1; + sy -= status_lines; - if (window_resize(c->session->window, c->sx, c->sy) != 0) - server_draw_client(c, 0, c->sy - 1); + if (window_resize(c->session->window, c->sx, sy) != 0) + server_draw_client(c); return (0); } -/* Keys message from client. */ int server_msg_fn_keys(struct hdr *hdr, struct client *c) { int key; size_t size; - if (c->session == NULL) - return (0); if (hdr->size & 0x1) fatalx("bad MSG_KEYS size"); + if (c->flags & CLIENT_HOLD) { + server_draw_client(c); + c->flags &= ~CLIENT_HOLD; + } + size = hdr->size; while (size != 0) { key = (int16_t) input_extract16(c->in); size -= 2; - if (c->prefix) { - cmd_dispatch(c, key); - c->prefix = 0; + if (c->flags & CLIENT_PREFIX) { + key_bindings_dispatch(key, c); + c->flags &= ~CLIENT_PREFIX; continue; } - if (key == cmd_prefix) - c->prefix = 1; + if (key == prefix_key) + c->flags |= CLIENT_PREFIX; else window_key(c->session->window, key); } return (0); } - -/* Sessions message from client. */ -int -server_msg_fn_sessions(struct hdr *hdr, struct client *c) -{ - struct sessions_data data; - struct sessions_entry entry; - struct session *s; - u_int i, j; - - if (hdr->size != sizeof data) - fatalx("bad MSG_SESSIONS size"); - buffer_read(c->in, &data, sizeof data); - - data.sessions = 0; - for (i = 0; i < ARRAY_LENGTH(&sessions); i++) { - if (ARRAY_ITEM(&sessions, i) != NULL) - data.sessions++; - } - server_write_client2(c, MSG_SESSIONS, - &data, sizeof data, NULL, data.sessions * sizeof entry); - - for (i = 0; i < ARRAY_LENGTH(&sessions); i++) { - s = ARRAY_ITEM(&sessions, i); - if (s == NULL) - continue; - strlcpy(entry.name, s->name, sizeof entry.name); - entry.tim = s->tim; - entry.windows = 0; - for (j = 0; j < ARRAY_LENGTH(&s->windows); j++) { - if (ARRAY_ITEM(&s->windows, j) != NULL) - entry.windows++; - } - buffer_write(c->out, &entry, sizeof entry); - } - - return (0); -} - -/* Windows message from client. */ -int -server_msg_fn_windows(struct hdr *hdr, struct client *c) -{ - struct windows_data data; - struct windows_entry entry; - struct session *s; - struct window *w; - u_int i; - char *cause; - - if (hdr->size != sizeof data) - fatalx("bad MSG_WINDOWS size"); - buffer_read(c->in, &data, sizeof data); - - if ((s = server_find_sessid(&data.sid, &cause)) == NULL) { - server_write_error(c, "%s", cause); - xfree(cause); - return (0); - } - - data.windows = 0; - for (i = 0; i < ARRAY_LENGTH(&s->windows); i++) { - if (ARRAY_ITEM(&s->windows, i) != NULL) - data.windows++; - } - server_write_client2(c, MSG_WINDOWS, - &data, sizeof data, NULL, data.windows * sizeof entry); - - for (i = 0; i < ARRAY_LENGTH(&s->windows); i++) { - w = ARRAY_ITEM(&s->windows, i); - if (w == NULL) - continue; - entry.idx = i; - strlcpy(entry.name, w->name, sizeof entry.name); - strlcpy(entry.title, w->screen.title, sizeof entry.title); - if (ttyname_r(w->fd, entry.tty, sizeof entry.tty) != 0) - *entry.tty = '\0'; - buffer_write(c->out, &entry, sizeof entry); - } - - return (0); -} - -/* Rename message from client. */ -int -server_msg_fn_rename(struct hdr *hdr, struct client *c) -{ - struct rename_data data; - char *cause; - struct window *w; - struct session *s; - u_int i; - - if (hdr->size != sizeof data) - fatalx("bad MSG_RENAME size"); - buffer_read(c->in, &data, sizeof data); - - data.newname[(sizeof data.newname) - 1] = '\0'; - if ((s = server_find_sessid(&data.sid, &cause)) == NULL) { - server_write_error(c, "%s", cause); - xfree(cause); - return (0); - } - - if (data.idx == -1) - w = s->window; - else { - if (data.idx < 0) - fatalx("bad window index"); - w = window_at(&s->windows, data.idx); - if (w == NULL) { - server_write_error(c, "window not found: %d", data.idx); - return (0); - } - } - - strlcpy(w->name, data.newname, sizeof w->name); - - server_write_client(c, MSG_OKAY, NULL, 0); - for (i = 0; i < ARRAY_LENGTH(&clients); i++) { - c = ARRAY_ITEM(&clients, i); - if (c != NULL && c->session != NULL) { - if (session_has(c->session, w)) - server_draw_status(c); - } - } - - return (0); -} - -/* Window list message from client */ -int -server_msg_fn_windowlist(struct hdr *hdr, struct client *c) -{ - struct window *w; - char *buf; - size_t len, off; - u_int i; - - if (c->session == NULL) - return (0); - if (hdr->size != 0) - fatalx("bad MSG_WINDOWLIST size"); - - len = c->sx + 1; - buf = xmalloc(len); - off = 0; - - *buf = '\0'; - for (i = 0; i < ARRAY_LENGTH(&c->session->windows); i++) { - w = ARRAY_ITEM(&c->session->windows, i); - if (w == NULL) - continue; - off += xsnprintf(buf + off, len - off, "%u:%s%s ", i, w->name, - w == c->session->window ? "*" : ""); - if (off >= len) - break; - } - - server_write_message(c, "%s", buf); - xfree(buf); - - return (0); -} - -/* Bind key message from client */ -int -server_msg_fn_bindkey(struct hdr *hdr, struct client *c) -{ - struct bind_data data; - const struct bind *bind; - char *str; - - if (hdr->size < sizeof data) - fatalx("bad MSG_BINDKEY size"); - buffer_read(c->in, &data, sizeof data); - - str = NULL; - if (data.flags & BIND_STRING) { - hdr->size -= sizeof data; - - str = xmemstrdup(BUFFER_OUT(c->in), hdr->size); - if (hdr->size > 0) - buffer_remove(c->in, hdr->size); - } - - data.cmd[(sizeof data.cmd) - 1] = '\0'; - if ((bind = cmd_lookup_bind(data.cmd)) == NULL) - fatalx("unknown command"); - if (!(bind->flags & BIND_USER) && - (data.flags & (BIND_NUMBER|BIND_STRING)) != 0) - fatalx("argument missing"); - if ((bind->flags & BIND_USER) && - (data.flags & (BIND_NUMBER|BIND_STRING)) == 0) - fatalx("argument required"); - - cmd_add_bind(data.key, data.num, str, bind); - if (str != NULL) - xfree(str); - - server_write_client(c, MSG_OKAY, NULL, 0); - - return (0); -} - -/* Unbind key message from client */ -int -server_msg_fn_unbindkey(struct hdr *hdr, struct client *c) -{ - struct bind_data data; - - if (hdr->size != sizeof data) - fatalx("bad MSG_UNBINDKEY size"); - - buffer_read(c->in, &data, hdr->size); - - cmd_remove_bind(data.key); - - server_write_client(c, MSG_OKAY, NULL, 0); - - return (0); -} diff --git a/server.c b/server.c index 8cdbd793..8fe25f5a 100644 --- a/server.c +++ b/server.c @@ -1,4 +1,4 @@ -/* $Id: server.c,v 1.19 2007-10-03 11:26:34 nicm Exp $ */ +/* $Id: server.c,v 1.20 2007-10-03 21:31:07 nicm Exp $ */ /* * Copyright (c) 2007 Nicholas Marriott @@ -127,7 +127,7 @@ server_main(char *srv_path, int srv_fd) ARRAY_INIT(&clients); ARRAY_INIT(&sessions); - cmd_init(); + key_bindings_init(); pfds = NULL; while (!sigterm) { @@ -171,7 +171,7 @@ server_main(char *srv_path, int srv_fd) server_handle_clients(&pfd); } - cmd_free(); + key_bindings_free(); close(srv_fd); unlink(srv_path); diff --git a/session.c b/session.c index a12f715e..69b528ed 100644 --- a/session.c +++ b/session.c @@ -1,4 +1,4 @@ -/* $Id: session.c,v 1.19 2007-09-29 21:02:26 nicm Exp $ */ +/* $Id: session.c,v 1.20 2007-10-03 21:31:07 nicm Exp $ */ /* * Copyright (c) 2007 Nicholas Marriott @@ -64,11 +64,10 @@ session_create(const char *name, const char *cmd, u_int sx, u_int sy) if (i == ARRAY_LENGTH(&sessions)) ARRAY_ADD(&sessions, s); - if (*name != '\0') - strlcpy(s->name, name, sizeof s->name); + if (name != NULL) + s->name = xstrdup(name); else - xsnprintf(s->name, sizeof s->name, "%u", i); - + xasprintf(&s->name, "%u", i); if (session_new(s, cmd, sx, sy) != 0) { session_destroy(s); return (NULL); @@ -92,6 +91,7 @@ session_destroy(struct session *s) while (!ARRAY_EMPTY(&s->windows)) window_remove(&s->windows, ARRAY_FIRST(&s->windows)); + xfree(s->name); xfree(s); } diff --git a/tmux.c b/tmux.c index 13dbbb0e..b47bff65 100644 --- a/tmux.c +++ b/tmux.c @@ -1,4 +1,4 @@ -/* $Id: tmux.c,v 1.24 2007-10-03 12:56:02 nicm Exp $ */ +/* $Id: tmux.c,v 1.25 2007-10-03 21:31:07 nicm Exp $ */ /* * Copyright (c) 2007 Nicholas Marriott @@ -19,7 +19,10 @@ #include #include +#include +#include #include +#include #include #include #include @@ -35,45 +38,29 @@ const char *malloc_options = "AFGJPX"; volatile sig_atomic_t sigwinch; volatile sig_atomic_t sigterm; int debug_level; +int prefix_key = META; u_int status_lines; char *default_command; void sighandler(int); -struct op { - const char *cmd; - const char *alias; - int (*fn)(char *, int, char **); -}; -const struct op op_table[] = { - { "attach", NULL, op_attach }, - { "bind-key", "bind", op_bind_key }, - { "list-sessions", "ls", op_list_sessions }, - { "list-windows", "lsw", op_list_windows }, - { "new-session", "new", op_new_session }, - { "rename-window", "renw", op_rename_window }, - { "unbind-key", "unbind", op_unbind_key }, -}; -#define NOP (sizeof op_table / sizeof op_table[0]) - -int -usage(const char *fmt, ...) +void +usage(char **ptr, const char *fmt, ...) { char *msg; va_list ap; if (fmt == NULL) { - fprintf(stderr, - "usage: %s [-v] [-S path] command [flags]\n", __progname); - return (1); - } + xasprintf(ptr, + "usage: %s [-v] [-S path] command [flags]", __progname); + } else { + va_start(ap, fmt); + xvasprintf(&msg, fmt, ap); + va_end(ap); - va_start(ap, fmt); - xvasprintf(&msg, fmt, ap); - va_end(ap); - fprintf(stderr, "usage: %s [-v] [-S path] %s\n", __progname, msg); - xfree(msg); - return (1); + xasprintf(ptr, "usage: %s [-v] [-S path] %s", __progname, msg); + xfree(msg); + } } void @@ -172,30 +159,39 @@ sigreset(void) int main(int argc, char **argv) { - const struct op *op, *found; + struct client_ctx cctx; + struct msg_command_data data; + struct buffer *b; + struct cmd *cmd; + struct pollfd pfd; + struct hdr hdr; const char *shell; - char *path; + char *path, *cause, name[MAXNAMELEN]; int opt; - u_int i; + *name = '\0'; path = NULL; - while ((opt = getopt(argc, argv, "S:v?")) != EOF) { + while ((opt = getopt(argc, argv, "S:s:v?")) != EOF) { switch (opt) { case 'S': path = xstrdup(optarg); break; + case 's': + if (strlcpy(name, optarg, sizeof name) >= sizeof name) + errx(1, "session name too long: %s", optarg); + break; case 'v': debug_level++; break; case '?': default: - exit(usage(NULL)); + goto usage; } } argc -= optind; argv += optind; if (argc == 0) - exit(usage(NULL)); + goto usage; log_open(stderr, LOG_USER, debug_level); @@ -206,21 +202,75 @@ main(int argc, char **argv) shell = "/bin/ksh"; xasprintf(&default_command, "exec %s -l", shell); - found = NULL; - for (i = 0; i < NOP; i++) { - op = op_table + i; - if (op->alias != NULL && strcmp(argv[0], op->alias) == 0) - exit(op->fn(path, argc, argv)); - if (strncmp(argv[0], op->cmd, strlen(argv[0])) == 0) { - if (found != NULL) { - log_warnx("ambiguous command: %s", argv[0]); - exit(1); - } - found = op; + if ((cmd = cmd_parse(argc, argv, &cause)) == NULL) { + if (cause == NULL) + goto usage; + log_warnx("%s", cause); + exit(1); + } + + if (!(cmd->entry->flags & CMD_NOSESSION)) + client_fill_sessid(&data.sid, name); + if (client_init(path, &cctx, cmd->entry->flags & CMD_STARTSERVER) != 0) + exit(1); + b = buffer_create(BUFSIZ); + cmd_send(cmd, b); + cmd_free(cmd); + + client_write_server2(&cctx, + MSG_COMMAND, &data, sizeof data, BUFFER_OUT(b), BUFFER_USED(b)); + buffer_destroy(b); + + for (;;) { + pfd.fd = cctx.srv_fd; + pfd.events = POLLIN; + if (BUFFER_USED(cctx.srv_out) > 0) + pfd.events |= POLLOUT; + + if (poll(&pfd, 1, INFTIM) == -1) { + if (errno == EAGAIN || errno == EINTR) + continue; + fatal("poll failed"); + } + + if (buffer_poll(&pfd, cctx.srv_in, cctx.srv_out) != 0) + fatalx("lost server"); + + restart: + if (BUFFER_USED(cctx.srv_in) < sizeof hdr) + continue; + memcpy(&hdr, BUFFER_OUT(cctx.srv_in), sizeof hdr); + if (BUFFER_USED(cctx.srv_in) < (sizeof hdr) + hdr.size) + continue; + buffer_remove(cctx.srv_in, sizeof hdr); + + switch (hdr.type) { + case MSG_EXIT: + exit(0); + case MSG_PRINT: + if (hdr.size > INT_MAX - 1) + fatalx("bad MSG_PRINT size"); + log_info( + "%.*s", (int) hdr.size, BUFFER_OUT(cctx.srv_in)); + buffer_remove(cctx.srv_in, hdr.size); + goto restart; + case MSG_ERROR: + if (hdr.size > INT_MAX - 1) + fatalx("bad MSG_ERROR size"); + log_warnx("%s: %.*s", __progname, + (int) hdr.size, BUFFER_OUT(cctx.srv_in)); + buffer_remove(cctx.srv_in, hdr.size); + exit(1); + case MSG_READY: + exit(client_main(&cctx)); + default: + fatalx("unexpected command"); } } - if (found != NULL) - exit(found->fn(path, argc, argv)); + /* NOTREACHED */ - exit(usage(NULL)); +usage: + usage(&cause, NULL); + fprintf(stderr, "%s\n", cause); + exit(1); } diff --git a/tmux.h b/tmux.h index 97b5df8c..0169b161 100644 --- a/tmux.h +++ b/tmux.h @@ -1,4 +1,4 @@ -/* $Id: tmux.h,v 1.37 2007-10-03 12:43:47 nicm Exp $ */ +/* $Id: tmux.h,v 1.38 2007-10-03 21:31:07 nicm Exp $ */ /* * Copyright (c) 2007 Nicholas Marriott @@ -259,28 +259,17 @@ struct buffer { /* Message codes. */ enum hdrtype { - MSG_ATTACH, - MSG_DATA, - MSG_DETACH, + MSG_COMMAND, MSG_ERROR, + MSG_PRINT, MSG_EXIT, + MSG_IDENTIFY, + MSG_READY, + MSG_DETACH, + MSG_RESIZE, + MSG_DATA, MSG_KEYS, - MSG_NEW, - MSG_OKAY, MSG_PAUSE, - MSG_RENAME, - MSG_SESSIONS, - MSG_SIZE, - MSG_WINDOWLIST, - MSG_WINDOWS, - MSG_BINDKEY, - MSG_UNBINDKEY, -}; - -/* Message header structure. */ -struct hdr { - enum hdrtype type; - size_t size; }; /* Session identification. */ @@ -290,61 +279,26 @@ struct sessid { char name[MAXNAMELEN]; /* empty for current */ }; -struct new_data { - char name[MAXNAMELEN]; +/* Message header structure. */ +struct hdr { + enum hdrtype type; + size_t size; +}; + +struct msg_command_data { + struct sessid sid; +}; + +struct msg_identify_data { u_int sx; u_int sy; }; -struct attach_data { - struct sessid sid; +struct msg_resize_data { u_int sx; u_int sy; }; -struct sessions_data { - u_int sessions; -}; - -struct sessions_entry { - char name[MAXNAMELEN]; - time_t tim; - u_int windows; -}; - -struct windows_data { - struct sessid sid; - u_int windows; -}; - -struct windows_entry { - u_int idx; - char tty[TTY_NAME_MAX]; - - char name[MAXNAMELEN]; - char title[MAXTITLELEN]; -}; - -struct size_data { - u_int sx; - u_int sy; -}; - -struct rename_data { - int idx; - struct sessid sid; - char newname[MAXNAMELEN]; -}; - -struct bind_data { - int key; - char cmd[MAXNAMELEN]; - - int flags; - - u_int num; -}; - /* Attributes. */ #define ATTR_BRIGHT 0x1 #define ATTR_DIM 0x2 @@ -460,7 +414,7 @@ ARRAY_DECL(windows, struct window *); /* Client session. */ struct session { - char name[MAXNAMELEN]; + char *name; time_t tim; struct window *window; @@ -478,7 +432,10 @@ struct client { u_int sx; u_int sy; - int prefix; /* waiting for command */ +#define CLIENT_TERMINAL 0x1 +#define CLIENT_PREFIX 0x2 +#define CLIENT_HOLD 0x4 + int flags; struct session *session; }; @@ -493,50 +450,85 @@ struct client_ctx { int loc_fd; struct buffer *loc_in; struct buffer *loc_out; - - struct winsize ws; }; -/* Key command. */ +/* Key/command line command. */ +enum cmd_type { + CMD_NEWSESSION, + CMD_DETACHSESSION, + CMD_LISTSESSIONS, +}; + +struct cmd_ctx { + struct client *client; + struct session *session; + + void (*print)(struct cmd_ctx *, const char *, ...); + void (*error)(struct cmd_ctx *, const char *, ...); + +#define CMD_KEY 0x1 + int flags; +}; + +struct cmd_entry { + enum cmd_type type; + const char *name; + const char *alias; + +#define CMD_STARTSERVER 0x1 +#define CMD_NOSESSION 0x2 + int flags; + + int (*parse)(void **, int, char **, char **); + const char *(*usage)(void); + void (*exec)(void *, struct cmd_ctx *); + void (*send)(void *, struct buffer *); + void (*recv)(void **, struct buffer *); + void (*free)(void *); +}; + struct cmd { - int key; - void (*fn)(struct client *, struct cmd *); - u_int num; - char *str; + const struct cmd_entry *entry; + void *data; }; /* Key binding. */ -struct bind { - const char *name; - void (*fn)(struct client *, struct cmd *); - -#define BIND_USER 0x1 -#define BIND_NUMBER 0x2 -#define BIND_STRING 0x4 - int flags; +struct binding { + int key; + struct cmd *cmd; }; /* tmux.c */ extern volatile sig_atomic_t sigwinch; extern volatile sig_atomic_t sigterm; +extern int prefix_key; extern int debug_level; extern u_int status_lines; extern char *default_command; -int usage(const char *, ...); +void usage(char **, const char *, ...); void logfile(const char *); void siginit(void); void sigreset(void); -/* op.c */ -int op_new_session(char *, int, char **); -int op_attach(char *, int, char **); -int op_rename_window(char *, int, char **); -int op_bind_key(char *, int, char **); -int op_unbind_key(char *, int, char **); +/* cmd.c */ +struct cmd *cmd_parse(int, char **, char **); +void cmd_exec(struct cmd *, struct cmd_ctx *); +void cmd_send(struct cmd *, struct buffer *); +struct cmd *cmd_recv(struct buffer *); +void cmd_free(struct cmd *); +void cmd_send_string(struct buffer *, const char *); +char *cmd_recv_string(struct buffer *); +extern const struct cmd_entry cmd_new_session_entry; +extern const struct cmd_entry cmd_detach_session_entry; +extern const struct cmd_entry cmd_list_sessions_entry; -/* op-list.c */ -int op_list_sessions(char *, int, char **); -int op_list_windows(char *, int, char **); +/* bind.c */ +const struct bind *cmdx_lookup_bind(const char *); +void cmdx_add_bind(int, u_int, char *, const struct bind *); +void cmdx_remove_bind(int); +void cmdx_init(void); +void cmdx_free(void); +void cmdx_dispatch(struct client *, int); /* client.c */ int client_init(char *, struct client_ctx *, int); @@ -552,14 +544,12 @@ void client_write_server2( struct client_ctx *, enum hdrtype, void *, size_t, void *, size_t); void client_fill_sessid(struct sessid *, char [MAXNAMELEN]); -/* cmd.c */ -extern int cmd_prefix; -const struct bind *cmd_lookup_bind(const char *); -void cmd_add_bind(int, u_int, char *, const struct bind *); -void cmd_remove_bind(int); -void cmd_init(void); -void cmd_free(void); -void cmd_dispatch(struct client *, int); +/* key-bindings.c */ +void key_bindings_add(int, struct cmd *); +void key_bindings_remove(int); +void key_bindings_init(void); +void key_bindings_free(void); +void key_bindings_dispatch(int, struct client *); /* key-string.c */ int key_string_lookup(const char *); @@ -582,7 +572,7 @@ void server_write_client2(struct client *, void server_write_clients( struct window *, enum hdrtype, const void *, size_t); void server_window_changed(struct client *); -void server_draw_client(struct client *, u_int, u_int); +void server_draw_client(struct client *); void server_draw_status(struct client *); /* status.c */