diff --git a/CHANGES b/CHANGES index 92dcfca6..2f84a8c9 100644 --- a/CHANGES +++ b/CHANGES @@ -1,5 +1,6 @@ 03 October 2007 +* (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. Possible commands are in cmd.c (look at cmd_bind_table). @@ -98,5 +99,5 @@ (including mutt, emacs). No status bar yet and no key remapping or other customisation. -$Id: CHANGES,v 1.24 2007-10-03 11:26:33 nicm Exp $ +$Id: CHANGES,v 1.25 2007-10-03 12:34:16 nicm Exp $ diff --git a/TODO b/TODO index 9b05a0b4..9ee064ee 100644 --- a/TODO +++ b/TODO @@ -34,6 +34,7 @@ packet... hrm. already scanning output for \e, could add an extra byte to it for message - could use bsearch all over the place +- better errors when creating new windows/sessions (how?) -- For 0.1 -------------------------------------------------------------------- - man page @@ -49,6 +50,7 @@ set status on/off set meta set shell + list-keys (list key bindings) - fix resize (width problems with multiple clients?) - handle tmux in tmux (check $TMUX and abort) - check for some reqd terminfo caps on startup diff --git a/client-fn.c b/client-fn.c index 8aecd463..37fde7a1 100644 --- a/client-fn.c +++ b/client-fn.c @@ -1,4 +1,4 @@ -/* $Id: client-fn.c,v 1.1 2007-10-03 10:18:31 nicm Exp $ */ +/* $Id: client-fn.c,v 1.2 2007-10-03 12:34:16 nicm Exp $ */ /* * Copyright (c) 2007 Nicholas Marriott @@ -62,6 +62,23 @@ client_write_server( hdr.type = type; hdr.size = len; buffer_write(cctx->srv_out, &hdr, sizeof hdr); - if (len > 0) + + if (buf != NULL) buffer_write(cctx->srv_out, buf, len); } + +void +client_write_server2(struct client_ctx *cctx, + enum hdrtype type, void *buf1, size_t len1, void *buf2, size_t len2) +{ + struct hdr hdr; + + hdr.type = type; + hdr.size = len1 + len2; + buffer_write(cctx->srv_out, &hdr, sizeof hdr); + + if (buf1 != NULL) + buffer_write(cctx->srv_out, buf1, len1); + if (buf2 != NULL) + buffer_write(cctx->srv_out, buf2, len2); +} diff --git a/cmd.c b/cmd.c index d7ae5c3d..54974909 100644 --- a/cmd.c +++ b/cmd.c @@ -1,4 +1,4 @@ -/* $Id: cmd.c,v 1.2 2007-10-03 11:26:34 nicm Exp $ */ +/* $Id: cmd.c,v 1.3 2007-10-03 12:34:16 nicm Exp $ */ /* * Copyright (c) 2007 Nicholas Marriott @@ -25,54 +25,49 @@ int cmd_prefix = META; -void cmd_fn_create(struct client *, int); -void cmd_fn_detach(struct client *, int); -void cmd_fn_last(struct client *, int); -void cmd_fn_meta(struct client *, int); -void cmd_fn_next(struct client *, int); -void cmd_fn_previous(struct client *, int); -void cmd_fn_refresh(struct client *, int); -void cmd_fn_select(struct client *, int); -void cmd_fn_windowinfo(struct client *, int); +void cmd_fn_create(struct client *, struct cmd *); +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_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_windowinfo(struct client *, struct cmd *); -struct cmd { - int key; - void (*fn)(struct client *, int); - int arg; -}; const struct cmd cmd_default[] = { - { '0', cmd_fn_select, 0 }, - { '1', cmd_fn_select, 1 }, - { '2', cmd_fn_select, 2 }, - { '3', cmd_fn_select, 3 }, - { '4', cmd_fn_select, 4 }, - { '5', cmd_fn_select, 5 }, - { '6', cmd_fn_select, 6 }, - { '7', cmd_fn_select, 7 }, - { '8', cmd_fn_select, 8 }, - { '9', cmd_fn_select, 9 }, - { 'C', cmd_fn_create, 0 }, - { 'c', cmd_fn_create, 0 }, - { 'D', cmd_fn_detach, 0 }, - { 'd', cmd_fn_detach, 0 }, - { 'N', cmd_fn_next, 0 }, - { 'n', cmd_fn_next, 0 }, - { 'P', cmd_fn_previous, 0 }, - { 'p', cmd_fn_previous, 0 }, - { 'R', cmd_fn_refresh, 0 }, - { 'r', cmd_fn_refresh, 0 }, - { 'L', cmd_fn_last, 0 }, - { 'l', cmd_fn_last, 0 }, - { 'I', cmd_fn_windowinfo, 0 }, - { 'i', cmd_fn_windowinfo, 0 }, - { META, cmd_fn_meta, 0 }, + { '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_create, 0, NULL }, + { 'c', cmd_fn_create, 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_windowinfo, 0, NULL }, + { 'i', cmd_fn_windowinfo, 0, NULL }, + { META, cmd_fn_meta, 0, NULL }, }; u_int cmd_count = (sizeof cmd_default / sizeof cmd_default[0]); struct cmd *cmd_table; const struct bind cmd_bind_table[] = { - { "select", cmd_fn_select, -1 }, - { "create", cmd_fn_create, 0 }, + { "select", cmd_fn_select, BIND_NUMBER|BIND_USER }, + { "create", cmd_fn_create, BIND_STRING|BIND_USER }, { "detach", cmd_fn_detach, 0 }, { "next", cmd_fn_next, 0 }, { "previous", cmd_fn_previous, 0 }, @@ -98,7 +93,7 @@ cmd_lookup_bind(const char *name) } void -cmd_add_bind(int key, int arg, const struct bind *bind) +cmd_add_bind(int key, u_int num, char *str, const struct bind *bind) { struct cmd *cmd = NULL; u_int i; @@ -124,10 +119,12 @@ cmd_add_bind(int key, int arg, const struct bind *bind) cmd->key = key; cmd->fn = bind->fn; - if (bind->arg != -1) - cmd->arg = bind->arg; - else - cmd->arg = arg; + if (bind->flags & BIND_USER) { + if (bind->flags & BIND_STRING) + cmd->str = xstrdup(str); + if (bind->flags & BIND_NUMBER) + cmd->num = num; + } } void @@ -155,6 +152,7 @@ cmd_init(void) void cmd_free(void) { + /* XXX free strings */ xfree(cmd_table); } @@ -167,35 +165,32 @@ cmd_dispatch(struct client *c, int key) for (i = 0; i < cmd_count; i++) { cmd = cmd_table + i; if (cmd->key != KEYC_NONE && cmd->key == key) - cmd->fn(c, cmd->arg); + cmd->fn(c, cmd); } } void -cmd_fn_create(struct client *c, unused int arg) +cmd_fn_create(struct client *c, struct cmd *cmd) { - const char *shell; - char *cmd; + char *s; - shell = getenv("SHELL"); - if (shell == NULL) - shell = "/bin/ksh"; - xasprintf(&cmd, "%s -l", shell); - if (session_new(c->session, cmd, c->sx, c->sy) != 0) - fatalx("session_new failed"); - xfree(cmd); - - server_draw_client(c, 0, c->sy - 1); + 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 int arg) +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 int arg) +cmd_fn_last(struct client *c, unused struct cmd *cmd) { if (session_last(c->session) == 0) server_window_changed(c); @@ -204,13 +199,13 @@ cmd_fn_last(struct client *c, unused int arg) } void -cmd_fn_meta(struct client *c, unused int arg) +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 int arg) +cmd_fn_next(struct client *c, unused struct cmd *cmd) { if (session_next(c->session) == 0) server_window_changed(c); @@ -219,7 +214,7 @@ cmd_fn_next(struct client *c, unused int arg) } void -cmd_fn_previous(struct client *c, unused int arg) +cmd_fn_previous(struct client *c, unused struct cmd *cmd) { if (session_previous(c->session) == 0) server_window_changed(c); @@ -228,22 +223,22 @@ cmd_fn_previous(struct client *c, unused int arg) } void -cmd_fn_refresh(struct client *c, unused int arg) +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, int arg) +cmd_fn_select(struct client *c, struct cmd *cmd) { - if (session_select(c->session, arg) == 0) + if (session_select(c->session, cmd->num) == 0) server_window_changed(c); else - server_write_message(c, "Window %u not present", arg); + server_write_message(c, "Window %u not present", cmd->num); } void -cmd_fn_windowinfo(struct client *c, unused int arg) +cmd_fn_windowinfo(struct client *c, unused struct cmd *cmd) { struct window *w; char *buf; diff --git a/op.c b/op.c index ef2a4de6..110eadb8 100644 --- a/op.c +++ b/op.c @@ -1,4 +1,4 @@ -/* $Id: op.c,v 1.10 2007-10-03 11:26:34 nicm Exp $ */ +/* $Id: op.c,v 1.11 2007-10-03 12:34:16 nicm Exp $ */ /* * Copyright (c) 2007 Nicholas Marriott @@ -168,7 +168,10 @@ op_bind_key(char *path, int argc, char **argv) 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) { @@ -190,19 +193,46 @@ op_bind_key(char *path, int argc, char **argv) return (1); } - if (argc == 3) { - data.arg = strtonum(argv[2], 0, INT_MAX, &errstr); - if (errstr != NULL) { - log_warnx("argument %s: %s", errstr, argv[2]); + 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); } - } else - data.arg = -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, INT_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_server(&cctx, MSG_BINDKEY, &data, sizeof data); + client_write_server2(&cctx, MSG_BINDKEY, &data, sizeof data, str, len); return (client_flush(&cctx)); } diff --git a/server-fn.c b/server-fn.c index 8048e8b7..f46a4715 100644 --- a/server-fn.c +++ b/server-fn.c @@ -1,4 +1,4 @@ -/* $Id: server-fn.c,v 1.12 2007-10-03 10:18:32 nicm Exp $ */ +/* $Id: server-fn.c,v 1.13 2007-10-03 12:34:16 nicm Exp $ */ /* * Copyright (c) 2007 Nicholas Marriott @@ -230,6 +230,8 @@ server_write_message(struct client *c, const char *fmt, ...) va_start(ap, fmt); xvasprintf(&msg, fmt, ap); va_end(ap); + if (strlen(msg) > c->sx - 1) + msg[c->sx - 1] = '\0'; buffer_write(c->out, msg, strlen(msg)); for (i = strlen(msg); i < c->sx; i++) input_store8(c->out, ' '); diff --git a/server-msg.c b/server-msg.c index 7c22d682..c402fc58 100644 --- a/server-msg.c +++ b/server-msg.c @@ -1,4 +1,4 @@ -/* $Id: server-msg.c,v 1.17 2007-10-03 11:26:34 nicm Exp $ */ +/* $Id: server-msg.c,v 1.18 2007-10-03 12:34:16 nicm Exp $ */ /* * Copyright (c) 2007 Nicholas Marriott @@ -88,14 +88,13 @@ int server_msg_fn_new(struct hdr *hdr, struct client *c) { struct new_data data; - const char *shell; - char *cmd, *msg; + char *msg; if (c->session != NULL) return (0); if (hdr->size != sizeof data) fatalx("bad MSG_NEW size"); - buffer_read(c->in, &data, hdr->size); + buffer_read(c->in, &data, sizeof data); c->sx = data.sx; if (c->sx == 0) @@ -115,14 +114,9 @@ server_msg_fn_new(struct hdr *hdr, struct client *c) return (0); } - shell = getenv("SHELL"); - if (shell == NULL) - shell = "/bin/ksh"; - xasprintf(&cmd, "%s -l", shell); - c->session = session_create(data.name, cmd, c->sx, c->sy); + c->session = session_create(data.name, default_command, c->sx, c->sy); if (c->session == NULL) fatalx("session_create failed"); - xfree(cmd); server_write_client(c, MSG_OKAY, NULL, 0); server_draw_client(c, 0, c->sy - 1); @@ -141,7 +135,7 @@ server_msg_fn_attach(struct hdr *hdr, struct client *c) return (0); if (hdr->size != sizeof data) fatalx("bad MSG_ATTACH size"); - buffer_read(c->in, &data, hdr->size); + buffer_read(c->in, &data, sizeof data); c->sx = data.sx; if (c->sx == 0) @@ -174,7 +168,7 @@ server_msg_fn_size(struct hdr *hdr, struct client *c) return (0); if (hdr->size != sizeof data) fatalx("bad MSG_SIZE size"); - buffer_read(c->in, &data, hdr->size); + buffer_read(c->in, &data, sizeof data); c->sx = data.sx; if (c->sx == 0) @@ -206,7 +200,7 @@ server_msg_fn_keys(struct hdr *hdr, struct client *c) size = hdr->size; while (size != 0) { - key = input_extract16(c->in); + key = (int16_t) input_extract16(c->in); size -= 2; if (c->prefix) { @@ -235,7 +229,7 @@ server_msg_fn_sessions(struct hdr *hdr, struct client *c) if (hdr->size != sizeof data) fatalx("bad MSG_SESSIONS size"); - buffer_read(c->in, &data, hdr->size); + buffer_read(c->in, &data, sizeof data); data.sessions = 0; for (i = 0; i < ARRAY_LENGTH(&sessions); i++) { @@ -275,7 +269,7 @@ server_msg_fn_windows(struct hdr *hdr, struct client *c) if (hdr->size != sizeof data) fatalx("bad MSG_WINDOWS size"); - buffer_read(c->in, &data, hdr->size); + buffer_read(c->in, &data, sizeof data); if ((s = server_find_sessid(&data.sid, &cause)) == NULL) { server_write_error(c, "%s", cause); @@ -318,8 +312,7 @@ server_msg_fn_rename(struct hdr *hdr, struct client *c) if (hdr->size != sizeof data) fatalx("bad MSG_RENAME size"); - - buffer_read(c->in, &data, hdr->size); + buffer_read(c->in, &data, sizeof data); data.newname[(sizeof data.newname) - 1] = '\0'; if ((s = server_find_sessid(&data.sid, &cause)) == NULL) { @@ -395,28 +388,40 @@ 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_BIND size"); + if (hdr->size < sizeof data) + fatalx("bad MSG_BINDKEY size"); + buffer_read(c->in, &data, sizeof data); - buffer_read(c->in, &data, hdr->size); + str = NULL; + if (data.flags & BIND_STRING) { + hdr->size -= sizeof data; + + if (hdr->size != 0) { + str = xmalloc(hdr->size + 1); + buffer_read(c->in, str, hdr->size); + str[hdr->size] = '\0'; + } + if (*str == '\0') { + xfree(str); + str = NULL; + } + } data.cmd[(sizeof data.cmd) - 1] = '\0'; - if ((bind = cmd_lookup_bind(data.cmd)) == NULL) { - server_write_error(c, "unknown command: %s", data.cmd); - return (0); - } - - if (bind->arg != -1 && data.arg != -1) { - server_write_error(c, "%s cannot have an argument", data.cmd); - return (0); - } - if (bind->arg == -1 && data.arg == -1) { - server_write_error(c, "%s requires an argument", data.cmd); - return (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.arg, bind); + cmd_add_bind(data.key, data.num, str, bind); + if (str != NULL) + xfree(str); server_write_client(c, MSG_OKAY, NULL, 0); @@ -430,7 +435,7 @@ server_msg_fn_unbindkey(struct hdr *hdr, struct client *c) struct bind_data data; if (hdr->size != sizeof data) - fatalx("bad MSG_UNBIND size"); + fatalx("bad MSG_UNBINDKEY size"); buffer_read(c->in, &data, hdr->size); diff --git a/tmux.c b/tmux.c index ca65489d..6b95f993 100644 --- a/tmux.c +++ b/tmux.c @@ -1,4 +1,4 @@ -/* $Id: tmux.c,v 1.21 2007-10-03 11:26:34 nicm Exp $ */ +/* $Id: tmux.c,v 1.22 2007-10-03 12:34:16 nicm Exp $ */ /* * Copyright (c) 2007 Nicholas Marriott @@ -36,6 +36,7 @@ volatile sig_atomic_t sigwinch; volatile sig_atomic_t sigterm; int debug_level; u_int status_lines; +char *default_command; void sighandler(int); @@ -172,6 +173,7 @@ int main(int argc, char **argv) { const struct op *op, *found; + const char *shell; char *path; int opt; u_int i; @@ -199,6 +201,11 @@ main(int argc, char **argv) status_lines = 1; + shell = getenv("SHELL"); + if (shell == NULL) + shell = "/bin/ksh"; + xasprintf(&default_command, "%s -l", shell); + found = NULL; for (i = 0; i < NOP; i++) { op = op_table + i; diff --git a/tmux.h b/tmux.h index d86e39c8..0d03dd06 100644 --- a/tmux.h +++ b/tmux.h @@ -1,4 +1,4 @@ -/* $Id: tmux.h,v 1.35 2007-10-03 11:26:34 nicm Exp $ */ +/* $Id: tmux.h,v 1.36 2007-10-03 12:34:16 nicm Exp $ */ /* * Copyright (c) 2007 Nicholas Marriott @@ -339,7 +339,10 @@ struct rename_data { struct bind_data { int key; char cmd[MAXNAMELEN]; - int arg; + + int flags; + + u_int num; }; /* Attributes. */ @@ -494,18 +497,31 @@ struct client_ctx { struct winsize ws; }; +/* Key command. */ +struct cmd { + int key; + void (*fn)(struct client *, struct cmd *); + u_int num; + char *str; +}; + /* Key binding. */ struct bind { const char *name; - void (*fn)(struct client *, int); - int arg; /* -1 if user specifies */ + void (*fn)(struct client *, struct cmd *); + +#define BIND_USER 0x1 +#define BIND_NUMBER 0x2 +#define BIND_STRING 0x4 + int flags; }; /* tmux.c */ extern volatile sig_atomic_t sigwinch; extern volatile sig_atomic_t sigterm; -extern int debug_level; -extern u_int status_lines; +extern int debug_level; +extern u_int status_lines; +extern char *default_command; int usage(const char *, ...); void logfile(const char *); void siginit(void); @@ -532,12 +548,14 @@ int client_msg_dispatch(struct client_ctx *, char **); /* client-fn.c */ void client_write_server(struct client_ctx *, enum hdrtype, void *, size_t); +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, int, const struct bind *); +void cmd_add_bind(int, u_int, char *, const struct bind *); void cmd_remove_bind(int); void cmd_init(void); void cmd_free(void);