hirc

IRC client
Log | Files | Refs

commit 8b9ec1063ef2e8711382334b20fc5e8e8930daa3
parent 12dcc76b59802cda402719ccb59e14bbc2f56dc6
Author: hhvn <dev@hhvn.uk>
Date:   Tue,  9 Nov 2021 16:56:28 +0000

commands.c config.h hirc.h main.c struct.h ui.c: command handling

Diffstat:
Mcommands.c | 253+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++----
Mconfig.h | 3+++
Mhirc.h | 5+++++
Mmain.c | 2+-
Mstruct.h | 13+++++++++++++
Mui.c | 2+-
6 files changed, 265 insertions(+), 13 deletions(-)

diff --git a/commands.c b/commands.c @@ -1,14 +1,239 @@ #include <ncurses.h> #include <stdlib.h> #include <string.h> +#include <unistd.h> +#include <pwd.h> +#include <sys/types.h> #include "hirc.h" +static char *command_optarg; +enum { + opt_error = -2, + opt_done = -1, + CMD_NARG, + CMD_ARG, +}; + +static struct Command commands[] = { + {"quit", command_quit}, + {"connect", command_connect}, + {"select", command_select}, + {NULL, NULL}, +}; + +void +command_quit(char *str) { + cleanup(str ? str : quitmessage); + exit(EXIT_SUCCESS); +} + +void +command_connect(char *str) { + struct Server *server; + char *network = NULL; + char *host = NULL; + char *port = NULL; + char *nick = NULL; + char *username = NULL; + char *realname = NULL; + int tls = 0, tls_verify = 0; + int ret; + struct passwd *user; + enum { + opt_network, + opt_nick, + opt_username, + opt_realname, +#ifdef TLS + opt_tls, + opt_tls_verify, +#endif /* TLS */ + }; + static struct CommandOpts opts[] = { + {"network", CMD_ARG, opt_network}, + {"nick", CMD_ARG, opt_nick}, + + {"username", CMD_ARG, opt_username}, + {"user", CMD_ARG, opt_username}, + + {"realname", CMD_ARG, opt_realname}, + {"real", CMD_ARG, opt_realname}, + {"comment", CMD_ARG, opt_realname}, + +#ifdef TLS + {"tls", CMD_NARG, opt_tls}, + {"ssl", CMD_NARG, opt_tls}, + {"verify", CMD_NARG, opt_tls_verify}, +#endif /* TLS */ + {NULL, 0, 0}, + }; + + while ((ret = command_getopt(&str, opts)) != opt_done) { + switch (ret) { + case opt_error: + return; + case opt_network: + network = command_optarg; + break; + case opt_nick: + nick = command_optarg; + break; + case opt_username: + username = command_optarg; + break; + case opt_realname: + realname = command_optarg; + break; +#ifdef TLS + case opt_tls: + tls = 1; + break; + case opt_tls_verify: + tls_verify = 1; + break; +#endif /* TLS */ + } + } + + host = strtok(str, " "); + port = strtok(NULL, " "); + + if (!host) { + ui_error("must specify host", NULL); + return; + } + + if (!nick) { + user = getpwuid(geteuid()); + nick = user ? user->pw_name : "null"; + } + + port = port ? port : "6667"; + username = username ? username : nick; + realname = realname ? realname : nick; + network = network ? network : host; + + server = serv_add(&servers, network, host, port, nick, username, realname, tls, tls_verify); + serv_connect(server); + ui_select(server, NULL); +} + +void +command_select(char *str) { + struct Server *sp; + struct Channel *chp; + char *server = NULL; + char *channel = NULL; + int ret, buf = 0; + enum { + opt_server, + opt_channel, + opt_test, + }; + static struct CommandOpts opts[] = { + {"server", CMD_ARG, opt_server}, + {"network", CMD_ARG, opt_server}, + {"channel", CMD_ARG, opt_channel}, + {NULL, 0, 0}, + }; + + while ((ret = command_getopt(&str, opts)) != opt_done) { + switch (ret) { + case opt_error: + return; + case opt_server: + server = command_optarg; + break; + case opt_channel: + channel = command_optarg; + break; + } + } + + if (server || channel) { + /* TODO: find closest match instead of perfect matches */ + if (!server) { + ui_error("must specify server and channel, or just server", NULL); + return; + } + + for (sp = servers; sp; sp = sp->next) + if (strcmp(sp->name, server) == 0) + break; + + if (!sp) { + ui_error("could not find server '%s'", server); + return; + } + + if (channel) { + for (chp = sp->channels; chp; chp = chp->next) + if (strcmp(chp->name, channel) == 0) + break; + + if (!chp) { + ui_error("could not find channel '%s'", channel); + return; + } + } else chp = NULL; + + ui_select(sp, chp); + + if (str) + ui_error("ignoring trailing arguments: '%s'", str); + } else { + buf = atoi(str); + if (!buf) + ui_error("invalid buffer index: '%s'", str); + ui_buflist_select(buf); + } +} + +int +command_getopt(char **str, struct CommandOpts *opts) { + char *opt; + + if (!str || !*str || **str != '-') + return opt_done; + + opt = struntil((*str)+1, ' '); + + for (; opts->opt; opts++) { + if (strcmp(opts->opt, opt) == 0) { + *str = strchr(*str, ' '); + if (*str) + (*str)++; + + if (opts->arg) { + command_optarg = *str; + if (*str) + *str = strchr(*str, ' '); + if (*str && **str) { + **str = '\0'; + (*str)++; + } + } else { + *((*str)-1) = '\0'; + } + + return opts->ret; + } + } + + ui_error("no such option '%s'", opt); + return opt_error; +} + void command_eval(char *str) { + struct Command *cmdp; char msg[512]; + char *cmd; char *s; if (*str != '/' || strncmp(str, "/ /", sizeof("/ /")) == 0) { + /* Provide a way to escape commands + * "/ /cmd" --> "/cmd" */ if (strncmp(str, "/ /", sizeof("/ /")) == 0) str += 3; @@ -21,19 +246,25 @@ command_eval(char *str) { ui_error("channel not selected, message ignored", NULL); return; - } - - if (strcmp(str, "/quit") == 0) { - endwin(); - exit(0); - } + } else { + str++; + cmd = str; + str = strchr(str, ' '); + if (str && *str) { + *str = '\0'; + str++; + if (*str == '\0') + str = NULL; + } - if (strncmp(str, "/select", strlen("/select")) == 0) { - if ((s = strchr(str, ' ')) != NULL) { - s++; - ui_buflist_select(atoi(s)); - return; + for (cmdp = commands; cmdp->name && cmdp->func; cmdp++) { + if (strcmp(cmdp->name, cmd) == 0) { + cmdp->func(str); + return; + } } + + ui_error("no such command: '%s'", cmd); } str++; diff --git a/config.h b/config.h @@ -149,4 +149,7 @@ static short buflistlocation = LEFT; /* width of buffer list in columns */ static int buflistwidth = 25; +/* default quit message */ +static char *quitmessage = "pain is temporary"; + #endif /* H_CONFIG */ diff --git a/hirc.h b/hirc.h @@ -92,6 +92,7 @@ void handle_NICK(char *msg, char **params, struct Server *server, time_t timest /* ui.c */ void ui_init(void); +#define ui_deinit() endwin() void ui_read(void); int ui_input_insert(char c, int counter); int ui_input_delete(int num, int counter); @@ -122,6 +123,10 @@ void ui_tls_error_(char *file, int line, struct tls *ctx, char *str); /* commands.c */ void command_eval(char *str); +int command_getopt(char **str, struct CommandOpts *opts); +void command_quit(char *str); +void command_connect(char *str); +void command_select(char *str); /* main.c */ extern struct Server *servers; diff --git a/main.c b/main.c @@ -205,7 +205,7 @@ main(int argc, char **argv) { main_buf->history = NULL; ui_init(); - ui_select(serv_add(&servers, "hlircnet", "irc.hhvn.uk", "6667", "hhvn", "Fanatic", "gopher://hhvn.uk", 1, 0), NULL); + ui_select(serv_add(&servers, "hlircnet", "localhost", "6667", "hhvn", "Fanatic", "gopher://hhvn.uk", 1, 0), NULL); /* serv_add(&servers, "dataswamp", "127.0.0.1", "6697", "hhvn", "Fanatic", "gopher://hhvn.uk", 1, 0); */ for (sp = servers; sp; sp = sp->next) serv_connect(sp); diff --git a/struct.h b/struct.h @@ -112,11 +112,24 @@ struct Server { struct Server *next; }; +/* messages received from server */ struct Handler { char *cmd; /* or numeric */ void (*func)(char *msg, char **params, struct Server *server, time_t timestamp); }; +/* commands received from user */ +struct Command { + char *name; + void (*func)(char *str); +}; + +struct CommandOpts { + char *opt; + int arg; + int ret; +}; + struct Netconfig { char *name; char *host; diff --git a/ui.c b/ui.c @@ -386,7 +386,7 @@ ui_buflist_select(int num) { ui_select(sp, NULL); return; } - i++; /* increment before moving + i++; /* increment before moving to channel section, not int for (;; ..) */