Only compile regex's once
This commit is contained in:
parent
4904128c98
commit
475c83618a
106
src/common.c
106
src/common.c
|
@ -1417,45 +1417,6 @@ set_boolean_if_valid (bool *out, const char *s)
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void
|
|
||||||
regerror_to_str (int code, const regex_t *preg, struct str *out)
|
|
||||||
{
|
|
||||||
size_t required = regerror (code, preg, NULL, 0);
|
|
||||||
str_ensure_space (out, required);
|
|
||||||
out->len += regerror (code, preg,
|
|
||||||
out->str + out->len, out->alloc - out->len) - 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
static size_t regex_error_domain_tag;
|
|
||||||
#define REGEX_ERROR (error_resolve_domain (®ex_error_domain_tag))
|
|
||||||
|
|
||||||
enum
|
|
||||||
{
|
|
||||||
REGEX_ERROR_COMPILATION_FAILED
|
|
||||||
};
|
|
||||||
|
|
||||||
static bool
|
|
||||||
regex_match (const char *regex, const char *s, struct error **e)
|
|
||||||
{
|
|
||||||
regex_t re;
|
|
||||||
int err = regcomp (&re, regex, REG_EXTENDED | REG_NOSUB);
|
|
||||||
if (err)
|
|
||||||
{
|
|
||||||
struct str desc;
|
|
||||||
|
|
||||||
str_init (&desc);
|
|
||||||
regerror_to_str (err, &re, &desc);
|
|
||||||
error_set (e, REGEX_ERROR, REGEX_ERROR_COMPILATION_FAILED,
|
|
||||||
"failed to compile regular expression: %s", desc.str);
|
|
||||||
str_free (&desc);
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool result = regexec (&re, s, 0, NULL, 0) != REG_NOMATCH;
|
|
||||||
regfree (&re);
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
static bool
|
static bool
|
||||||
read_line (FILE *fp, struct str *s)
|
read_line (FILE *fp, struct str *s)
|
||||||
{
|
{
|
||||||
|
@ -1512,6 +1473,73 @@ xssl_get_error (SSL *ssl, int result, const char **error_info)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// --- Regular expressions -----------------------------------------------------
|
||||||
|
|
||||||
|
static size_t regex_error_domain_tag;
|
||||||
|
#define REGEX_ERROR (error_resolve_domain (®ex_error_domain_tag))
|
||||||
|
|
||||||
|
enum
|
||||||
|
{
|
||||||
|
REGEX_ERROR_COMPILATION_FAILED
|
||||||
|
};
|
||||||
|
|
||||||
|
static regex_t *
|
||||||
|
regex_compile (const char *regex, int flags, struct error **e)
|
||||||
|
{
|
||||||
|
regex_t *re = xmalloc (sizeof *re);
|
||||||
|
int err = regcomp (re, regex, flags);
|
||||||
|
if (!err)
|
||||||
|
return re;
|
||||||
|
|
||||||
|
struct str desc;
|
||||||
|
str_init (&desc);
|
||||||
|
|
||||||
|
size_t required = regerror (err, re, NULL, 0);
|
||||||
|
str_ensure_space (&desc, required);
|
||||||
|
desc.len += regerror (err, re,
|
||||||
|
desc.str + desc.len, desc.alloc - desc.len) - 1;
|
||||||
|
|
||||||
|
free (re);
|
||||||
|
error_set (e, REGEX_ERROR, REGEX_ERROR_COMPILATION_FAILED,
|
||||||
|
"%s: %s", "failed to compile regular expression", desc.str);
|
||||||
|
str_free (&desc);
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
regex_free (void *regex)
|
||||||
|
{
|
||||||
|
regfree (regex);
|
||||||
|
free (regex);
|
||||||
|
}
|
||||||
|
|
||||||
|
// The cost of hashing a string is likely to be significantly smaller than that
|
||||||
|
// of compiling the whole regular expression anew, so here is a simple cache.
|
||||||
|
// Adding basic support for subgroups is easy: check `re_nsub' and output into
|
||||||
|
// a `struct str_vector' (if all we want is the substrings).
|
||||||
|
|
||||||
|
static void
|
||||||
|
regex_cache_init (struct str_map *cache)
|
||||||
|
{
|
||||||
|
str_map_init (cache);
|
||||||
|
cache->free = regex_free;
|
||||||
|
}
|
||||||
|
|
||||||
|
static bool
|
||||||
|
regex_cache_match (struct str_map *cache, const char *regex, int flags,
|
||||||
|
const char *s, struct error **e)
|
||||||
|
{
|
||||||
|
regex_t *re = str_map_find (cache, regex);
|
||||||
|
if (!re)
|
||||||
|
{
|
||||||
|
re = regex_compile (regex, flags, e);
|
||||||
|
if (!re)
|
||||||
|
return false;
|
||||||
|
str_map_set (cache, regex, re);
|
||||||
|
}
|
||||||
|
return regexec (re, s, 0, NULL, 0) != REG_NOMATCH;
|
||||||
|
}
|
||||||
|
|
||||||
// --- IRC utilities -----------------------------------------------------------
|
// --- IRC utilities -----------------------------------------------------------
|
||||||
|
|
||||||
struct irc_message
|
struct irc_message
|
||||||
|
|
45
src/kike.c
45
src/kike.c
|
@ -103,13 +103,24 @@ enum validation_result
|
||||||
#define IRC_NICKNAME_MAX 9
|
#define IRC_NICKNAME_MAX 9
|
||||||
#define IRC_HOSTNAME_MAX 63
|
#define IRC_HOSTNAME_MAX 63
|
||||||
|
|
||||||
// Anything to keep it as short as possible
|
static bool
|
||||||
#define SN "[0-9A-Za-z][-0-9A-Za-z]*[0-9A-Za-z]*"
|
irc_regex_match (const char *regex, const char *s)
|
||||||
#define N4 "[0-9]{1,3}"
|
{
|
||||||
#define N6 "[0-9ABCDEFabcdef]{1,}"
|
static struct str_map cache;
|
||||||
|
static bool initialized;
|
||||||
|
|
||||||
#define LE "A-Za-z"
|
if (!initialized)
|
||||||
#define SP "\\[\\]\\\\`_^{|}"
|
{
|
||||||
|
regex_cache_init (&cache);
|
||||||
|
initialized = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
struct error *e = NULL;
|
||||||
|
bool result = regex_cache_match (&cache, regex,
|
||||||
|
REG_EXTENDED | REG_NOSUB, s, &e);
|
||||||
|
hard_assert (!e);
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
static const char *
|
static const char *
|
||||||
irc_validate_to_str (enum validation_result result)
|
irc_validate_to_str (enum validation_result result)
|
||||||
|
@ -124,14 +135,20 @@ irc_validate_to_str (enum validation_result result)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO: at least cache the resulting `regex_t' in a `struct str_map'
|
// Anything to keep it as short as possible
|
||||||
|
#define SN "[0-9A-Za-z][-0-9A-Za-z]*[0-9A-Za-z]*"
|
||||||
|
#define N4 "[0-9]{1,3}"
|
||||||
|
#define N6 "[0-9ABCDEFabcdef]{1,}"
|
||||||
|
|
||||||
|
#define LE "A-Za-z"
|
||||||
|
#define SP "\\[\\]\\\\`_^{|}"
|
||||||
|
|
||||||
static enum validation_result
|
static enum validation_result
|
||||||
irc_validate_hostname (const char *hostname)
|
irc_validate_hostname (const char *hostname)
|
||||||
{
|
{
|
||||||
if (!*hostname)
|
if (!*hostname)
|
||||||
return VALIDATION_ERROR_EMPTY;
|
return VALIDATION_ERROR_EMPTY;
|
||||||
if (!regex_match ("^" SN "(\\." SN ")*$", hostname, NULL))
|
if (!irc_regex_match ("^" SN "(\\." SN ")*$", hostname))
|
||||||
return VALIDATION_ERROR_INVALID;
|
return VALIDATION_ERROR_INVALID;
|
||||||
if (strlen (hostname) > IRC_HOSTNAME_MAX)
|
if (strlen (hostname) > IRC_HOSTNAME_MAX)
|
||||||
return VALIDATION_ERROR_TOO_LONG;
|
return VALIDATION_ERROR_TOO_LONG;
|
||||||
|
@ -141,11 +158,11 @@ irc_validate_hostname (const char *hostname)
|
||||||
static bool
|
static bool
|
||||||
irc_is_valid_hostaddr (const char *hostaddr)
|
irc_is_valid_hostaddr (const char *hostaddr)
|
||||||
{
|
{
|
||||||
if (regex_match ("^" N4 "\\." N4 "\\." N4 "\\." N4 "$", hostaddr, NULL)
|
if (irc_regex_match ("^" N4 "\\." N4 "\\." N4 "\\." N4 "$", hostaddr)
|
||||||
|| regex_match ("^" N6 ":" N6 ":" N6 ":" N6 ":"
|
|| irc_regex_match ("^" N6 ":" N6 ":" N6 ":" N6 ":"
|
||||||
N6 ":" N6 ":" N6 ":" N6 "$", hostaddr, NULL)
|
N6 ":" N6 ":" N6 ":" N6 "$", hostaddr)
|
||||||
|| regex_match ("^0:0:0:0:0:(0|[Ff]{4}):"
|
|| irc_regex_match ("^0:0:0:0:0:(0|[Ff]{4}):"
|
||||||
N4 "\\." N4 "\\." N4 "\\." N4 "$", hostaddr, NULL))
|
N4 "\\." N4 "\\." N4 "\\." N4 "$", hostaddr))
|
||||||
return true;
|
return true;
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
@ -162,7 +179,7 @@ irc_validate_nickname (const char *nickname)
|
||||||
{
|
{
|
||||||
if (!*nickname)
|
if (!*nickname)
|
||||||
return VALIDATION_ERROR_EMPTY;
|
return VALIDATION_ERROR_EMPTY;
|
||||||
if (!regex_match ("^[" LE SP "][-0-9" LE SP "]*$", nickname, NULL))
|
if (!irc_regex_match ("^[" LE SP "][-0-9" LE SP "]*$", nickname))
|
||||||
return VALIDATION_ERROR_INVALID;
|
return VALIDATION_ERROR_INVALID;
|
||||||
if (strlen (nickname) > IRC_NICKNAME_MAX)
|
if (strlen (nickname) > IRC_NICKNAME_MAX)
|
||||||
return VALIDATION_ERROR_TOO_LONG;
|
return VALIDATION_ERROR_TOO_LONG;
|
||||||
|
|
|
@ -118,6 +118,7 @@ enum
|
||||||
struct bot_context
|
struct bot_context
|
||||||
{
|
{
|
||||||
struct str_map config; ///< User configuration
|
struct str_map config; ///< User configuration
|
||||||
|
regex_t *admin_re; ///< Regex to match our administrator
|
||||||
|
|
||||||
int irc_fd; ///< Socket FD of the server
|
int irc_fd; ///< Socket FD of the server
|
||||||
struct str read_buffer; ///< Input yet to be processed
|
struct str read_buffer; ///< Input yet to be processed
|
||||||
|
@ -140,6 +141,7 @@ bot_context_init (struct bot_context *self)
|
||||||
str_map_init (&self->config);
|
str_map_init (&self->config);
|
||||||
self->config.free = free;
|
self->config.free = free;
|
||||||
load_config_defaults (&self->config, g_config_table);
|
load_config_defaults (&self->config, g_config_table);
|
||||||
|
self->admin_re = NULL;
|
||||||
|
|
||||||
self->irc_fd = -1;
|
self->irc_fd = -1;
|
||||||
str_init (&self->read_buffer);
|
str_init (&self->read_buffer);
|
||||||
|
@ -160,6 +162,8 @@ static void
|
||||||
bot_context_free (struct bot_context *self)
|
bot_context_free (struct bot_context *self)
|
||||||
{
|
{
|
||||||
str_map_free (&self->config);
|
str_map_free (&self->config);
|
||||||
|
if (self->admin_re)
|
||||||
|
regex_free (self->admin_re);
|
||||||
str_free (&self->read_buffer);
|
str_free (&self->read_buffer);
|
||||||
|
|
||||||
// TODO: terminate the plugins properly before this is called
|
// TODO: terminate the plugins properly before this is called
|
||||||
|
@ -1110,25 +1114,10 @@ is_private_message (const struct irc_message *msg)
|
||||||
static bool
|
static bool
|
||||||
is_sent_by_admin (struct bot_context *ctx, const struct irc_message *msg)
|
is_sent_by_admin (struct bot_context *ctx, const struct irc_message *msg)
|
||||||
{
|
{
|
||||||
const char *admin = str_map_find (&ctx->config, "admin");
|
|
||||||
|
|
||||||
// No administrator set -> everyone is an administrator
|
// No administrator set -> everyone is an administrator
|
||||||
if (!admin)
|
if (!ctx->admin_re)
|
||||||
return true;
|
return true;
|
||||||
|
return regexec (ctx->admin_re, msg->prefix, 0, NULL, 0) != REG_NOMATCH;
|
||||||
// TODO: precompile the regex
|
|
||||||
struct error *e = NULL;
|
|
||||||
if (regex_match (admin, msg->prefix, NULL))
|
|
||||||
return true;
|
|
||||||
|
|
||||||
if (e)
|
|
||||||
{
|
|
||||||
print_error ("%s: %s", "invalid admin mask", e->message);
|
|
||||||
error_free (e);
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
return false;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static void respond_to_user (struct bot_context *ctx, const struct
|
static void respond_to_user (struct bot_context *ctx, const struct
|
||||||
|
@ -1591,6 +1580,26 @@ irc_connect (struct bot_context *ctx, struct error **e)
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static bool
|
||||||
|
load_admin_regex (struct bot_context *ctx)
|
||||||
|
{
|
||||||
|
hard_assert (!ctx->admin_re);
|
||||||
|
const char *admin = str_map_find (&ctx->config, "admin");
|
||||||
|
|
||||||
|
if (!admin)
|
||||||
|
return true;
|
||||||
|
|
||||||
|
struct error *e = NULL;
|
||||||
|
ctx->admin_re = regex_compile (admin, REG_EXTENDED | REG_NOSUB, &e);
|
||||||
|
if (!e)
|
||||||
|
return true;
|
||||||
|
|
||||||
|
print_error ("invalid configuration value for `%s': %s",
|
||||||
|
"admin", e->message);
|
||||||
|
error_free (e);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
static void
|
static void
|
||||||
on_signal_pipe_readable (const struct pollfd *fd, struct bot_context *ctx)
|
on_signal_pipe_readable (const struct pollfd *fd, struct bot_context *ctx)
|
||||||
{
|
{
|
||||||
|
@ -1758,6 +1767,8 @@ main (int argc, char *argv[])
|
||||||
(poller_dispatcher_func) on_signal_pipe_readable, &ctx);
|
(poller_dispatcher_func) on_signal_pipe_readable, &ctx);
|
||||||
|
|
||||||
plugin_load_all_from_config (&ctx);
|
plugin_load_all_from_config (&ctx);
|
||||||
|
if (!load_admin_regex (&ctx))
|
||||||
|
exit (EXIT_FAILURE);
|
||||||
if (!irc_connect (&ctx, &e))
|
if (!irc_connect (&ctx, &e))
|
||||||
{
|
{
|
||||||
print_error ("%s", e->message);
|
print_error ("%s", e->message);
|
||||||
|
|
Loading…
Reference in New Issue