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:
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 (;; ..) */