commit f754545c7dde051898a0231933a42e93de53af74
parent 356a686a20feb3be4a8fbf9ba345e52751707f26
Author: hhvn <dev@hhvn.uk>
Date: Sun, 20 Feb 2022 19:41:55 +0000
Autocommand handling
Diffstat:
7 files changed, 209 insertions(+), 39 deletions(-)
diff --git a/src/commands.c b/src/commands.c
@@ -155,8 +155,15 @@ struct Command commands[] = {
"Set a formatting variable.",
"This is equivalent to /set format.<format> string...", NULL}},
{"server", command_server, 0, {
- "usage: /server <server> cmd....",
- "Run (non-raw) command with server as target.", NULL}},
+ "usage: /server [-auto] <server> [cmd....]",
+ " /server [-clear] <server>",
+ "Evaluate a cooked command with server as target.",
+ " -auto if supplied with a command, run that command",
+ " automatically when the server connects.",
+ " Otherwise, list autocmds that have been set.",
+ " -clear clear autocmds from server",
+ "To send a raw command to a server, use:",
+ " /server <server> /quote ...", NULL}},
{"names", command_names, 1, {
"usage: /names <channel>",
"List nicks in channel (pretty useless with nicklist.", NULL}},
@@ -205,6 +212,7 @@ struct Command commands[] = {
"usage: /dump [-all] [-aliases] [-bindings] [-formats] [-config]",
" [-default] [-servers] [-channels] [-queries] <file>",
"Dumps configuration details into a file.",
+ " -autocmds dump commands specified with /server -auto",
" -aliases dump /alias commands",
" -bindings dump /bind commands",
" -formats dump /format commands beginning with filter.",
@@ -214,7 +222,9 @@ struct Command commands[] = {
" -queries dump /query commands for respective servers",
" -default dump default settings (dump non-default otherwise)",
"If none (excluding -default) of the above are selected, it is",
- "treated as though all are selected.", NULL}},
+ "treated as though all are selected.",
+ "If -autocmds and -channels are used together, and there exists",
+ "an autocmd to join a channel, then only the autocmd will be dumped.", NULL}},
{"close", command_close, 0, {
"usage: /close [id]",
"Forget about selected buffer, or a buffer by id.", NULL}},
@@ -378,7 +388,7 @@ command_query(struct Server *server, char *str) {
if ((priv = chan_get(&server->privs, str, -1)) == NULL)
priv = chan_add(server, &server->privs, str, 1);
- if (!readingconf)
+ if (!nouich)
ui_select(server, priv);
}
@@ -694,7 +704,7 @@ command_connect(struct Server *server, char *str) {
tserver = serv_add(&servers, network, host, port, nick, username, realname, tls, tls_verify);
serv_connect(tserver);
- if (!readingconf)
+ if (!nouich)
ui_select(tserver, NULL);
}
@@ -834,12 +844,35 @@ static void
command_server(struct Server *server, char *str) {
struct Server *nserver;
char *tserver, *cmd, *arg;
- int i;
+ char **acmds;
+ int i, ret, mode;
+ enum { opt_norm, opt_auto, opt_clear };
+ struct CommandOpts opts[] = {
+ {"auto", CMD_NARG, opt_auto},
+ {"clear", CMD_NARG, opt_clear},
+ {NULL, 0, 0},
+ };
+
+ mode = opt_norm;
+ while ((ret = command_getopt(&str, opts)) != opt_done) {
+ switch (ret) {
+ case opt_error:
+ return;
+ case opt_auto:
+ case opt_clear:
+ if (mode != opt_norm) {
+ ui_error("conflicting flags", NULL);
+ return;
+ }
+ mode = ret;
+ }
+ }
tserver = strtok_r(str, " ", &arg);
- cmd = strtok_r(NULL, " ", &arg);
+ if (mode == opt_norm)
+ cmd = strtok_r(NULL, " ", &arg);
- if (!tserver || !cmd) {
+ if (!tserver) {
command_toofew("server");
return;
}
@@ -849,17 +882,53 @@ command_server(struct Server *server, char *str) {
return;
}
- if (*cmd == '/')
- cmd++;
-
- for (i=0; commands[i].name && commands[i].func; i++) {
- if (strcmp(commands[i].name, cmd) == 0) {
- commands[i].func(nserver, arg);
+ switch (mode) {
+ case opt_norm:
+ if (!cmd || !*cmd) {
+ command_toofew("server");
return;
}
- }
- ui_error("no such commands: '%s'", cmd);
+ if (*cmd == '/')
+ cmd++;
+
+ for (i=0; commands[i].name && commands[i].func; i++) {
+ if (strcmp(commands[i].name, cmd) == 0) {
+ commands[i].func(nserver, arg);
+ return;
+ }
+ }
+ ui_error("no such commands: '%s'", cmd);
+ break;
+ case opt_auto:
+ if (!arg || !*arg) {
+ hist_format(selected.history, Activity_none, HIST_SHOW|HIST_TMP|HIST_MAIN, "SELF_AUTOCMDS_START %s :Autocmds for %s:",
+ nserver->name, nserver->name);
+ for (acmds = nserver->autocmds; acmds && *acmds; acmds++)
+ hist_format(selected.history, Activity_none, HIST_SHOW|HIST_TMP|HIST_MAIN, "SELF_AUTOCMDS_LIST %s :%s",
+ nserver->name, *acmds);
+ hist_format(selected.history, Activity_none, HIST_SHOW|HIST_TMP|HIST_MAIN, "SELF_AUTOCMDS_END %s :End of autocmds for %s",
+ nserver->name, nserver->name);
+ } else {
+ if (*arg == '/') {
+ cmd = arg;
+ } else {
+ cmd = emalloc(strlen(arg) + 2);
+ snprintf(cmd, strlen(arg) + 2, "/%s", arg);
+ }
+
+ serv_auto_add(nserver, cmd);
+ }
+ break;
+ case opt_clear:
+ if (*arg) {
+ command_toomany("server");
+ break;
+ }
+
+ serv_auto_free(nserver);
+ break;
+ }
}
static void
@@ -1259,6 +1328,7 @@ command_dump(struct Server *server, char *str) {
int selected = 0;
int def = 0, ret;
int i;
+ char **aup;
struct Server *sp;
struct Channel *chp;
struct Alias *ap;
@@ -1271,7 +1341,8 @@ command_dump(struct Server *server, char *str) {
opt_servers = 16,
opt_channels = 32,
opt_queries = 64,
- opt_default = 128,
+ opt_autocmds = 128,
+ opt_default = 256,
};
static struct CommandOpts opts[] = {
{"aliases", CMD_NARG, opt_aliases},
@@ -1279,6 +1350,7 @@ command_dump(struct Server *server, char *str) {
{"formats", CMD_NARG, opt_formats},
{"config", CMD_NARG, opt_config},
{"servers", CMD_NARG, opt_servers},
+ {"autocmds", CMD_NARG, opt_autocmds},
{"channels", CMD_NARG, opt_channels},
{"queries", CMD_NARG, opt_queries},
{"default", CMD_NARG, opt_default},
@@ -1295,6 +1367,7 @@ command_dump(struct Server *server, char *str) {
case opt_config:
case opt_servers:
case opt_channels:
+ case opt_autocmds:
selected |= ret;
break;
case opt_default:
@@ -1304,7 +1377,7 @@ command_dump(struct Server *server, char *str) {
}
if (!selected)
- selected = 127;
+ selected = opt_default - 1;
if (!str || !*str) {
command_toofew("dump");
@@ -1316,7 +1389,7 @@ command_dump(struct Server *server, char *str) {
return;
}
- if (selected & opt_servers || selected & opt_channels || selected & opt_queries) {
+ if (selected & (opt_servers|opt_channels|opt_queries|opt_autocmds)) {
if (selected & opt_servers)
fprintf(file, "Network connections\n");
@@ -1335,9 +1408,14 @@ command_dump(struct Server *server, char *str) {
sp->host,
sp->port);
}
+ if (selected & opt_autocmds) {
+ for (aup = sp->autocmds; *aup; aup++)
+ fprintf(file, "/server -auto %s %s\n", sp->name, *aup);
+ }
if (selected & opt_channels) {
for (chp = sp->channels; chp; chp = chp->next)
- fprintf(file, "/server %s /join %s\n", sp->name, chp->name);
+ if (!(selected & opt_autocmds) || !serv_auto_haschannel(sp, chp->name))
+ fprintf(file, "/server %s /join %s\n", sp->name, chp->name);
}
if (selected & opt_queries) {
for (chp = sp->privs; chp; chp = chp->next)
@@ -1546,7 +1624,7 @@ alias_eval(char *cmd) {
}
void
-command_eval(char *str) {
+command_eval(struct Server *server, char *str) {
struct Command *cmdp;
char msg[512];
char *cmd;
@@ -1582,10 +1660,10 @@ command_eval(char *str) {
for (cmdp = commands; cmdp->name && cmdp->func; cmdp++) {
if (strcmp(cmdp->name, cmd) == 0) {
- if (cmdp->needserver && !selected.server) {
+ if (cmdp->needserver && !server) {
ui_error("/%s requires a server to be selected or provided by /server", cmdp->name);
} else {
- cmdp->func(selected.server, s);
+ cmdp->func(server, s);
}
return;
}
diff --git a/src/config.c b/src/config.c
@@ -25,8 +25,6 @@
#include <limits.h>
#include "hirc.h"
-int readingconf = 0;
-
static int config_nicklist_location(long num);
static int config_nicklist_width(long num);
static int config_buflist_location(long num);
@@ -277,6 +275,21 @@ struct Config config[] = {
.strhandle = config_redraws,
.description = {
"Format of footer of /bind output", NULL}},
+ {"format.ui.autocmds", 1, Val_string,
+ .str = " ${2}",
+ .strhandle = config_redraws,
+ .description = {
+ "Format of /server -auto output", NULL}},
+ {"format.ui.autocmds.start", 1, Val_string,
+ .str = "Autocmds for ${1}:",
+ .strhandle = config_redraws,
+ .description = {
+ "Format of header of /server -auto output", NULL}},
+ {"format.ui.autocmds.end", 1, Val_string,
+ .str = "",
+ .strhandle = config_redraws,
+ .description = {
+ "Format of footer of /server -auto output", NULL}},
{"format.ui.grep.start", 1, Val_string,
.str = "%{b}%{c:94}Results of ${1}:",
.strhandle = config_redraws,
@@ -1358,8 +1371,7 @@ config_read(char *filename) {
if (!bt)
bt = emalloc((sizeof(char *)) * (btoffset + 1));
else
- bt = realloc(bt, (sizeof(char *)) * (btoffset + 1));
- assert(bt != NULL);
+ bt = erealloc(bt, (sizeof(char *)) * (btoffset + 1));
*(bt + btoffset) = path;
btoffset++;
@@ -1370,13 +1382,13 @@ config_read(char *filename) {
goto shrink;
}
- save = readingconf;
- readingconf = 1;
+ save = nouich;
+ nouich = 1;
while (read_line(fileno(file), buf, sizeof(buf)))
if (*buf == '/')
- command_eval(buf);
+ command_eval(NULL, buf);
fclose(file);
- readingconf = save;
+ nouich = save;
shrink:
/* Remove path from bt and shrink */
@@ -1386,7 +1398,7 @@ shrink:
free(bt);
bt = NULL;
} else {
- bt = realloc(bt, (sizeof(char *)) * btoffset);
+ bt = erealloc(bt, (sizeof(char *)) * btoffset);
assert(bt != NULL);
}
}
diff --git a/src/handle.c b/src/handle.c
@@ -554,14 +554,20 @@ handle_RPL_TOPICWHOTIME(struct Server *server, struct History *msg) {
static void
handle_RPL_WELCOME(struct Server *server, struct History *msg) {
- server->status = ConnStatus_connected;
+ if (server->status != ConnStatus_connected) {
+ server->status = ConnStatus_connected;
+ serv_auto_send(server);
+ }
hist_addp(server->history, msg, Activity_status, HIST_DFL);
}
static void
handle_RPL_ENDOFMOTD(struct Server *server, struct History *msg) {
/* If server doesn't support RPL_WELCOME, use RPL_ENDOFMOTD to set status */
- server->status = ConnStatus_connected;
+ if (server->status != ConnStatus_connected) {
+ server->status = ConnStatus_connected;
+ serv_auto_send(server);
+ }
hist_addp(server->history, msg, Activity_status, HIST_DFL);
}
diff --git a/src/hirc.h b/src/hirc.h
@@ -109,6 +109,10 @@ int serv_remove(struct Server **head, char *name);
int serv_selected(struct Server *server);
void serv_disconnect(struct Server *server, int reconnect, char *msg);
int serv_ischannel(struct Server *server, char *str);
+void serv_auto_add(struct Server *server, char *cmd);
+void serv_auto_free(struct Server *server);
+void serv_auto_send(struct Server *server);
+int serv_auto_haschannel(struct Server *server, char *chan);
char * support_get(struct Server *server, char *key);
void support_set(struct Server *server, char *key, char *value);
void schedule_push(struct Server *server, char *tmsg, char *msg);
@@ -156,7 +160,7 @@ void ui_tls_error_(char *file, int line, const char *func, struct tls *ctx, cha
#endif /* TLS */
/* commands.c */
-void command_eval(char *str);
+void command_eval(struct Server *server, char *str);
int command_getopt(char **str, struct CommandOpts *opts);
int alias_add(char *binding, char *cmd);
int alias_remove(char *binding);
@@ -182,10 +186,10 @@ extern struct Selected selected;
extern struct Keybind *keybinds;
extern struct Window windows[Win_last];
extern int uineedredraw;
+extern int nouich;
/* config.c */
extern struct Config config[];
-extern int readingconf;
/* commands.c */
extern struct Command commands[];
diff --git a/src/serv.c b/src/serv.c
@@ -102,6 +102,7 @@ serv_create(char *name, char *host, char *port, char *nick,
server->reconnect = 0;
for (i=0; i < Expect_last; i++)
server->expect[i] = NULL;
+ server->autocmds = NULL;
server->connectfail = 0;
server->lastconnected = server->lastrecv = server->pingsent = 0;
@@ -451,6 +452,70 @@ serv_ischannel(struct Server *server, char *str) {
}
void
+serv_auto_add(struct Server *server, char *cmd) {
+ char **p;
+ size_t len;
+
+ if (!server || !cmd)
+ return;
+
+ if (!server->autocmds) {
+ len = 1;
+ server->autocmds = emalloc(sizeof(char *) * (len + 1));
+ } else {
+ for (p = server->autocmds, len = 1; *p; p++)
+ len++;
+ server->autocmds = erealloc(server->autocmds, sizeof(char *) * (len + 1));
+ }
+
+ *(server->autocmds + len - 1) = estrdup(cmd);
+ *(server->autocmds + len) = NULL;
+}
+
+void
+serv_auto_free(struct Server *server) {
+ char **p;
+
+ if (!server || !server->autocmds)
+ return;
+
+ for (p = server->autocmds; *p; p++)
+ free(*p);
+ free(server->autocmds);
+ server->autocmds = NULL;
+}
+
+void
+serv_auto_send(struct Server *server) {
+ char **p;
+ int save;
+
+ if (!server || !server->autocmds)
+ return;
+
+ save = nouich;
+ nouich = 1;
+ for (p = server->autocmds; *p; p++)
+ command_eval(server, *p);
+ nouich = save;
+}
+
+/* check if autocmds has '/join <chan>' */
+int
+serv_auto_haschannel(struct Server *server, char *chan) {
+ char **p;
+
+ if (!server || !server->autocmds)
+ return 0;
+
+ for (p = server->autocmds; *p; p++)
+ if (strncmp(*p, "/join ", strlen("/join ")) == 0 &&
+ strcmp((*p) + strlen("/join "), chan) == 0)
+ return 1;
+ return 0;
+}
+
+void
schedule_push(struct Server *server, char *tmsg, char *msg) {
struct Schedule *p;
@@ -513,7 +578,7 @@ schedule_pull(struct Server *server, char *tmsg) {
void
expect_set(struct Server *server, enum Expect cmd, char *about) {
- if (cmd >= Expect_last || cmd < 0 || readingconf)
+ if (cmd >= Expect_last || cmd < 0 || nouich)
return;
free(server->expect[cmd]);
diff --git a/src/struct.h b/src/struct.h
@@ -150,6 +150,7 @@ struct Server {
struct Schedule *schedule;
int reconnect;
char *expect[Expect_last];
+ char **autocmds;
int connectfail; /* number of failed connections */
time_t lastconnected; /* last time a connection was lost */
time_t lastrecv; /* last time a message was received from server */
diff --git a/src/ui.c b/src/ui.c
@@ -30,6 +30,7 @@
#include "hirc.h"
int uineedredraw = 0;
+int nouich = 0;
#define HIRC_COLOURS 100
static unsigned short colourmap[HIRC_COLOURS] = {
@@ -89,6 +90,9 @@ struct {
{"SELF_HELP_START", "format.ui.help.start"},
{"SELF_HELP", "format.ui.help"},
{"SELF_HELP_END", "format.ui.help.end"},
+ {"SELF_AUTOCMDS_START", "format.ui.autocmds.start"},
+ {"SELF_AUTOCMDS_LIST", "format.ui.autocmds"},
+ {"SELF_AUTOCMDS_END", "format.ui.autocmds.end"},
/* Real commands/numerics from server */
{"PRIVMSG", "format.privmsg"},
{"NOTICE", "format.notice"},
@@ -394,7 +398,7 @@ ui_read(void) {
for (kp = keybinds; kp; kp = kp->next) {
if ((input.counter - savecounter) == strlen(kp->binding) &&
strncmp(kp->binding, &input.string[savecounter], (input.counter - savecounter)) == 0) {
- command_eval(kp->cmd);
+ command_eval(selected.server, kp->cmd);
memmove(&input.string[savecounter],
&input.string[input.counter],
strlen(&input.string[input.counter]) + 1);
@@ -457,7 +461,7 @@ ui_read(void) {
break;
case KEY_ENTER:
case '\r':
- command_eval(input.string);
+ command_eval(selected.server, input.string);
/* free checks for null */
free(input.history[INPUT_HIST_MAX - 1]);
memmove(input.history + 1, input.history, (sizeof(input.history) / INPUT_HIST_MAX) * (INPUT_HIST_MAX - 1));