Key binding, unbinding.

pull/1/head
Nicholas Marriott 2007-10-03 11:26:34 +00:00
parent a68b1e58db
commit dadc56d754
10 changed files with 514 additions and 31 deletions

View File

@ -1,5 +1,8 @@
03 October 2007
* (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).
* (nicm) Move command parsing into the client. Also rename some messages and
tidy up a few bits. Lots more tidying up needed :-/.
@ -95,5 +98,5 @@
(including mutt, emacs). No status bar yet and no key remapping or other
customisation.
$Id: CHANGES,v 1.23 2007-10-03 10:18:31 nicm Exp $
$Id: CHANGES,v 1.24 2007-10-03 11:26:33 nicm Exp $

View File

@ -1,4 +1,4 @@
# $Id: Makefile,v 1.11 2007-10-03 10:18:31 nicm Exp $
# $Id: Makefile,v 1.12 2007-10-03 11:26:34 nicm Exp $
.SUFFIXES: .c .o .y .h
.PHONY: clean
@ -18,7 +18,8 @@ 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
session.c local.c log.c client.c client-msg.c client-fn.c op.c op-list.c \
key-string.c
YACC= yacc -d

2
TODO
View File

@ -33,6 +33,7 @@
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
-- For 0.1 --------------------------------------------------------------------
- man page
@ -48,7 +49,6 @@
set status on/off
set meta
set shell
bind key??
- fix resize (width problems with multiple clients?)
- handle tmux in tmux (check $TMUX and abort)
- check for some reqd terminfo caps on startup

101
cmd.c
View File

@ -1,4 +1,4 @@
/* $Id: cmd.c,v 1.1 2007-10-03 10:18:32 nicm Exp $ */
/* $Id: cmd.c,v 1.2 2007-10-03 11:26:34 nicm Exp $ */
/*
* Copyright (c) 2007 Nicholas Marriott <nicm@users.sourceforge.net>
@ -19,6 +19,7 @@
#include <sys/types.h>
#include <stdlib.h>
#include <string.h>
#include "tmux.h"
@ -39,8 +40,7 @@ struct cmd {
void (*fn)(struct client *, int);
int arg;
};
struct cmd cmd_table[] = {
const struct cmd cmd_default[] = {
{ '0', cmd_fn_select, 0 },
{ '1', cmd_fn_select, 1 },
{ '2', cmd_fn_select, 2 },
@ -67,7 +67,96 @@ struct cmd cmd_table[] = {
{ 'i', cmd_fn_windowinfo, 0 },
{ META, cmd_fn_meta, 0 },
};
#define NCMD (sizeof cmd_table / sizeof cmd_table[0])
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 },
{ "detach", cmd_fn_detach, 0 },
{ "next", cmd_fn_next, 0 },
{ "previous", cmd_fn_previous, 0 },
{ "refresh", cmd_fn_refresh, 0 },
{ "last", cmd_fn_last, 0 },
{ "window-info",cmd_fn_windowinfo, 0 },
{ "meta", cmd_fn_meta, 0 }
};
#define NCMDBIND (sizeof cmd_bind_table / sizeof cmd_bind_table[0])
const struct bind *
cmd_lookup_bind(const char *name)
{
const struct bind *bind;
u_int i;
for (i = 0; i < NCMDBIND; i++) {
bind = cmd_bind_table + i;
if (strcmp(bind->name, name) == 0)
return (bind);
}
return (NULL);
}
void
cmd_add_bind(int key, int arg, const struct bind *bind)
{
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->arg != -1)
cmd->arg = bind->arg;
else
cmd->arg = arg;
}
void
cmd_remove_bind(int key)
{
struct cmd *cmd;
u_int i;
for (i = 0; i < cmd_count; i++) {
cmd = cmd_table + i;
if (cmd->key == key) {
cmd->key = KEYC_NONE;
break;
}
}
}
void
cmd_init(void)
{
cmd_table = xmalloc(sizeof cmd_default);
memcpy(cmd_table, cmd_default, sizeof cmd_default);
}
void
cmd_free(void)
{
xfree(cmd_table);
}
void
cmd_dispatch(struct client *c, int key)
@ -75,9 +164,9 @@ cmd_dispatch(struct client *c, int key)
struct cmd *cmd;
u_int i;
for (i = 0; i < NCMD; i++) {
for (i = 0; i < cmd_count; i++) {
cmd = cmd_table + i;
if (cmd->key == key)
if (cmd->key != KEYC_NONE && cmd->key == key)
cmd->fn(c, cmd->arg);
}
}

223
key-string.c Normal file
View File

@ -0,0 +1,223 @@
/* $Id: key-string.c,v 1.1 2007-10-03 11:26:34 nicm Exp $ */
/*
* Copyright (c) 2007 Nicholas Marriott <nicm@users.sourceforge.net>
*
* 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 <sys/types.h>
#include <string.h>
#include "tmux.h"
struct {
const char *string;
int key;
} key_string_table[] = {
{ "A1", KEYC_A1 },
{ "A3", KEYC_A3 },
{ "B2", KEYC_B2 },
{ "BACKSPACE", KEYC_BACKSPACE },
{ "BEG", KEYC_BEG },
{ "BTAB", KEYC_BTAB },
{ "C1", KEYC_C1 },
{ "C3", KEYC_C3 },
{ "CANCEL", KEYC_CANCEL },
{ "CATAB", KEYC_CATAB },
{ "CLEAR", KEYC_CLEAR },
{ "CLOSE", KEYC_CLOSE },
{ "COMMAND", KEYC_COMMAND },
{ "COPY", KEYC_COPY },
{ "CREATE", KEYC_CREATE },
{ "CTAB", KEYC_CTAB },
{ "DC", KEYC_DC },
{ "DL", KEYC_DL },
{ "DOWN", KEYC_DOWN},
{ "EIC", KEYC_EIC },
{ "END", KEYC_END },
{ "ENTER", KEYC_ENTER },
{ "EOL", KEYC_EOL },
{ "EOS", KEYC_EOS },
{ "EXIT", KEYC_EXIT },
{ "F0", KEYC_F0 },
{ "F1", KEYC_F1 },
{ "F10", KEYC_F10 },
{ "F11", KEYC_F11 },
{ "F12", KEYC_F12 },
{ "F13", KEYC_F13 },
{ "F14", KEYC_F14 },
{ "F15", KEYC_F15 },
{ "F16", KEYC_F16 },
{ "F17", KEYC_F17 },
{ "F18", KEYC_F18 },
{ "F19", KEYC_F19 },
{ "F2", KEYC_F2 },
{ "F20", KEYC_F20 },
{ "F21", KEYC_F21 },
{ "F22", KEYC_F22 },
{ "F23", KEYC_F23 },
{ "F24", KEYC_F24 },
{ "F25", KEYC_F25 },
{ "F26", KEYC_F26 },
{ "F27", KEYC_F27 },
{ "F28", KEYC_F28 },
{ "F29", KEYC_F29 },
{ "F3", KEYC_F3 },
{ "F30", KEYC_F30 },
{ "F31", KEYC_F31 },
{ "F32", KEYC_F32 },
{ "F33", KEYC_F33 },
{ "F34", KEYC_F34 },
{ "F35", KEYC_F35 },
{ "F36", KEYC_F36 },
{ "F37", KEYC_F37 },
{ "F38", KEYC_F38 },
{ "F39", KEYC_F39 },
{ "F4", KEYC_F4 },
{ "F40", KEYC_F40 },
{ "F41", KEYC_F41 },
{ "F42", KEYC_F42 },
{ "F43", KEYC_F43 },
{ "F44", KEYC_F44 },
{ "F45", KEYC_F45 },
{ "F46", KEYC_F46 },
{ "F47", KEYC_F47 },
{ "F48", KEYC_F48 },
{ "F49", KEYC_F49 },
{ "F5", KEYC_F5 },
{ "F50", KEYC_F50 },
{ "F51", KEYC_F51 },
{ "F52", KEYC_F52 },
{ "F53", KEYC_F53 },
{ "F54", KEYC_F54 },
{ "F55", KEYC_F55 },
{ "F56", KEYC_F56 },
{ "F57", KEYC_F57 },
{ "F58", KEYC_F58 },
{ "F59", KEYC_F59 },
{ "F6", KEYC_F6 },
{ "F60", KEYC_F60 },
{ "F61", KEYC_F61 },
{ "F62", KEYC_F62 },
{ "F63", KEYC_F63 },
{ "F7", KEYC_F7 },
{ "F8", KEYC_F8 },
{ "F9", KEYC_F9 },
{ "FIND", KEYC_FIND },
{ "HELP", KEYC_HELP },
{ "HOME", KEYC_HOME },
{ "IC", KEYC_IC },
{ "IL", KEYC_IL },
{ "LEFT", KEYC_LEFT },
{ "LL", KEYC_LL },
{ "MARK", KEYC_MARK },
{ "MESSAGE", KEYC_MESSAGE },
{ "MOVE", KEYC_MOVE },
{ "NEXT", KEYC_NEXT },
{ "NPAGE", KEYC_NPAGE },
{ "OPEN", KEYC_OPEN },
{ "OPTIONS", KEYC_OPTIONS },
{ "PPAGE", KEYC_PPAGE },
{ "PREVIOUS", KEYC_PREVIOUS },
{ "PRINT", KEYC_PRINT },
{ "REDO", KEYC_REDO },
{ "REFERENCE", KEYC_REFERENCE },
{ "REFRESH", KEYC_REFRESH },
{ "REPLACE", KEYC_REPLACE },
{ "RESTART", KEYC_RESTART },
{ "RESUME", KEYC_RESUME },
{ "RIGHT", KEYC_RIGHT },
{ "SAVE", KEYC_SAVE },
{ "SBEG", KEYC_SBEG },
{ "SCANCEL", KEYC_SCANCEL },
{ "SCOMMAND", KEYC_SCOMMAND },
{ "SCOPY", KEYC_SCOPY },
{ "SCREATE", KEYC_SCREATE },
{ "SDC", KEYC_SDC },
{ "SDL", KEYC_SDL },
{ "SELECT", KEYC_SELECT },
{ "SEND", KEYC_SEND },
{ "SEOL", KEYC_SEOL },
{ "SEXIT", KEYC_SEXIT },
{ "SF", KEYC_SF },
{ "SFIND", KEYC_SFIND },
{ "SHELP", KEYC_SHELP },
{ "SHOME", KEYC_SHOME },
{ "SIC", KEYC_SIC },
{ "SLEFT", KEYC_SLEFT },
{ "SMESSAGE", KEYC_SMESSAGE },
{ "SMOVE", KEYC_SMOVE },
{ "SNEXT", KEYC_SNEXT },
{ "SOPTIONS", KEYC_SOPTIONS },
{ "SPREVIOUS", KEYC_SPREVIOUS },
{ "SPRINT", KEYC_SPRINT },
{ "SR", KEYC_SR },
{ "SREDO", KEYC_SREDO },
{ "SREPLACE", KEYC_SREPLACE },
{ "SRIGHT", KEYC_SRIGHT },
{ "SRSUME", KEYC_SRSUME },
{ "SSAVE", KEYC_SSAVE },
{ "SSUSPEND", KEYC_SSUSPEND },
{ "STAB", KEYC_STAB },
{ "SUNDO", KEYC_SUNDO },
{ "SUSPEND", KEYC_SUSPEND },
{ "UNDO", KEYC_UNDO },
{ "UP", KEYC_UP },
{ "^@", 0 },
{ "^A", 1 },
{ "^B", 2 },
{ "^C", 3 },
{ "^D", 4 },
{ "^E", 5 },
{ "^F", 6 },
{ "^G", 7 },
{ "^H", 8 },
{ "^I", 9 },
{ "^J", 10 },
{ "^K", 11 },
{ "^L", 12 },
{ "^M", 13 },
{ "^N", 14 },
{ "^O", 15 },
{ "^P", 16 },
{ "^Q", 17 },
{ "^R", 18 },
{ "^S", 19 },
{ "^T", 20 },
{ "^U", 21 },
{ "^V", 22 },
{ "^W", 23 },
{ "^X", 24 },
{ "^Y", 25 },
{ "^Z", 26 },
};
#define NKEYSTRINGS (sizeof key_string_table / sizeof key_string_table[0])
int
key_string_lookup(const char *string)
{
u_int i;
if (string[0] == '\0')
return (KEYC_NONE);
if (string[1] == '\0')
return (string[0]);
for (i = 0; i < NKEYSTRINGS; i++) {
if (strcasecmp(string, key_string_table[i].string) == 0)
return (key_string_table[i].key);
}
return (KEYC_NONE);
}

80
op.c
View File

@ -1,4 +1,4 @@
/* $Id: op.c,v 1.9 2007-09-29 14:57:07 nicm Exp $ */
/* $Id: op.c,v 1.10 2007-10-03 11:26:34 nicm Exp $ */
/*
* Copyright (c) 2007 Nicholas Marriott <nicm@users.sourceforge.net>
@ -160,3 +160,81 @@ op_rename(char *path, int argc, char **argv)
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;
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 (argc == 3) {
data.arg = strtonum(argv[2], 0, INT_MAX, &errstr);
if (errstr != NULL) {
log_warnx("argument %s: %s", errstr, argv[2]);
return (1);
}
} else
data.arg = -1;
if (client_init(path, &cctx, 1) != 0)
return (1);
client_write_server(&cctx, MSG_BINDKEY, &data, sizeof data);
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));
}

View File

@ -1,4 +1,4 @@
/* $Id: server-msg.c,v 1.16 2007-10-03 10:18:32 nicm Exp $ */
/* $Id: server-msg.c,v 1.17 2007-10-03 11:26:34 nicm Exp $ */
/*
* Copyright (c) 2007 Nicholas Marriott <nicm@users.sourceforge.net>
@ -25,11 +25,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_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 *);
@ -40,11 +42,13 @@ struct server_msg {
};
const struct server_msg server_msg_table[] = {
{ MSG_ATTACH, server_msg_fn_attach },
{ MSG_BINDKEY, server_msg_fn_bindkey },
{ 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 },
};
@ -317,7 +321,7 @@ server_msg_fn_rename(struct hdr *hdr, struct client *c)
buffer_read(c->in, &data, hdr->size);
data.newname[(sizeof data.newname) - 1] = '\0';
data.newname[(sizeof data.newname) - 1] = '\0';
if ((s = server_find_sessid(&data.sid, &cause)) == NULL) {
server_write_error(c, "%s", cause);
xfree(cause);
@ -384,3 +388,55 @@ server_msg_fn_windowlist(struct hdr *hdr, struct client *c)
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;
if (hdr->size != sizeof data)
fatalx("bad MSG_BIND size");
buffer_read(c->in, &data, hdr->size);
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);
}
cmd_add_bind(data.key, data.arg, bind);
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_UNBIND size");
buffer_read(c->in, &data, hdr->size);
cmd_remove_bind(data.key);
server_write_client(c, MSG_OKAY, NULL, 0);
return (0);
}

View File

@ -1,4 +1,4 @@
/* $Id: server.c,v 1.18 2007-10-03 10:20:33 nicm Exp $ */
/* $Id: server.c,v 1.19 2007-10-03 11:26:34 nicm Exp $ */
/*
* Copyright (c) 2007 Nicholas Marriott <nicm@users.sourceforge.net>
@ -127,6 +127,8 @@ server_main(char *srv_path, int srv_fd)
ARRAY_INIT(&clients);
ARRAY_INIT(&sessions);
cmd_init();
pfds = NULL;
while (!sigterm) {
/* Initialise pollfd array. */
@ -169,6 +171,8 @@ server_main(char *srv_path, int srv_fd)
server_handle_clients(&pfd);
}
cmd_free();
close(srv_fd);
unlink(srv_path);

23
tmux.c
View File

@ -1,4 +1,4 @@
/* $Id: tmux.c,v 1.20 2007-10-03 09:17:00 nicm Exp $ */
/* $Id: tmux.c,v 1.21 2007-10-03 11:26:34 nicm Exp $ */
/*
* Copyright (c) 2007 Nicholas Marriott <nicm@users.sourceforge.net>
@ -50,15 +50,28 @@ const struct op op_table[] = {
{ "list-windows", "lsw", op_list_windows },
{ "new-session", "new", op_new/*_session*/ },
{ "rename-window", "renw", op_rename },
{ "bind-key", "bind", op_bind_key },
{ "unbind-key", "unbind", op_unbind_key },
};
#define NOP (sizeof op_table / sizeof op_table[0])
int
usage(const char *s)
usage(const char *fmt, ...)
{
if (s == NULL)
s = "command [flags]";
fprintf(stderr, "usage: %s [-v] [-S path] %s\n", __progname, s);
char *msg;
va_list ap;
if (fmt == NULL) {
fprintf(stderr,
"usage: %s [-v] [-S path] command [flags]\n", __progname);
return (1);
}
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);
}

40
tmux.h
View File

@ -1,4 +1,4 @@
/* $Id: tmux.h,v 1.34 2007-10-03 10:18:32 nicm Exp $ */
/* $Id: tmux.h,v 1.35 2007-10-03 11:26:34 nicm Exp $ */
/*
* Copyright (c) 2007 Nicholas Marriott <nicm@users.sourceforge.net>
@ -31,7 +31,7 @@
#include "array.h"
extern cc_t ttydefchars[];
extern char *__progname;
extern char *__progname;
#define MAXNAMELEN 32
#define MAXTITLELEN 192
@ -273,6 +273,8 @@ enum hdrtype {
MSG_SIZE,
MSG_WINDOWLIST,
MSG_WINDOWS,
MSG_BINDKEY,
MSG_UNBINDKEY,
};
/* Message header structure. */
@ -328,21 +330,18 @@ struct size_data {
u_int sy;
};
struct select_data {
u_int idx;
};
struct refresh_data {
u_int py_upper;
u_int py_lower;
};
struct rename_data {
int idx;
struct sessid sid;
char newname[MAXNAMELEN];
};
struct bind_data {
int key;
char cmd[MAXNAMELEN];
int arg;
};
/* Attributes. */
#define ATTR_BRIGHT 0x1
#define ATTR_DIM 0x2
@ -495,12 +494,19 @@ struct client_ctx {
struct winsize ws;
};
/* Key binding. */
struct bind {
const char *name;
void (*fn)(struct client *, int);
int arg; /* -1 if user specifies */
};
/* tmux.c */
extern volatile sig_atomic_t sigwinch;
extern volatile sig_atomic_t sigterm;
extern int debug_level;
extern u_int status_lines;
int usage(const char *);
int usage(const char *, ...);
void logfile(const char *);
void siginit(void);
void sigreset(void);
@ -509,6 +515,8 @@ void sigreset(void);
int op_new(char *, int, char **);
int op_attach(char *, int, char **);
int op_rename(char *, int, char **);
int op_bind_key(char *, int, char **);
int op_unbind_key(char *, int, char **);
/* op-list.c */
int op_list_sessions(char *, int, char **);
@ -528,8 +536,16 @@ 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_remove_bind(int);
void cmd_init(void);
void cmd_free(void);
void cmd_dispatch(struct client *, int);
/* key-string.c */
int key_string_lookup(const char *);
/* server.c */
extern struct clients clients;
int server_start(char *);