From 7fa873fb964156e71a00174f50e3f4bc343bcc04 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?P=C5=99emysl=20Janouch?=
Date: Sat, 11 Jul 2015 17:44:37 +0200
Subject: [PATCH] Import stuff from uirc3
---
liberty-proto.c | 17 ++-
liberty.c | 380 +++++++++++++++++++++++++++++-------------------
2 files changed, 241 insertions(+), 156 deletions(-)
diff --git a/liberty-proto.c b/liberty-proto.c
index c2ea64c..1b5d53f 100644
--- a/liberty-proto.c
+++ b/liberty-proto.c
@@ -66,7 +66,7 @@ irc_parse_message_tags (const char *tags, struct str_map *out)
{
struct str_vector v;
str_vector_init (&v);
- split_str_ignore_empty (tags, ';', &v);
+ cstr_split_ignore_empty (tags, ';', &v);
for (size_t i = 0; i < v.len; i++)
{
@@ -191,15 +191,18 @@ irc_tolower (int c)
return c >= 'A' && c <= 'Z' ? c + ('a' - 'A') : c;
}
-static size_t
-irc_strxfrm (char *dest, const char *src, size_t n)
+static int
+irc_tolower_strict (int c)
{
- size_t len = strlen (src);
- while (n-- && (*dest++ = irc_tolower (*src++)))
- ;
- return len;
+ if (c == '[') return '{';
+ if (c == ']') return '}';
+ if (c == '\\') return '|';
+ return c >= 'A' && c <= 'Z' ? c + ('a' - 'A') : c;
}
+TRIVIAL_STRXFRM (irc_strxfrm, irc_tolower)
+TRIVIAL_STRXFRM (irc_strxfrm_strict, irc_tolower_strict)
+
static int
irc_strcmp (const char *a, const char *b)
{
diff --git a/liberty.c b/liberty.c
index a9f30aa..85e9a2b 100644
--- a/liberty.c
+++ b/liberty.c
@@ -314,6 +314,15 @@ xstrndup (const char *s, size_t n)
(tail) = (link); \
BLOCK_END
+#define LIST_INSERT_WITH_TAIL(head, tail, link, following) \
+ BLOCK_START \
+ if (following) \
+ LIST_APPEND_WITH_TAIL ((head), (following)->prev, (link)); \
+ else \
+ LIST_APPEND_WITH_TAIL ((head), (tail), (link)); \
+ (link)->next = (following); \
+ BLOCK_END
+
#define LIST_UNLINK_WITH_TAIL(head, tail, link) \
BLOCK_START \
if ((tail) == (link)) \
@@ -1468,6 +1477,12 @@ poller_timer_set (struct poller_timer *self, int timeout_ms)
poller_timers_set (self->timers, self);
}
+static bool
+poller_timer_is_active (struct poller_timer *self)
+{
+ return self->index != -1;
+}
+
static void
poller_timer_reset (struct poller_timer *self)
{
@@ -1790,21 +1805,31 @@ msg_writer_flush (struct msg_writer *self, size_t *len)
// --- ASCII -------------------------------------------------------------------
+#define TRIVIAL_STRXFRM(name, fn) \
+ static size_t \
+ name (char *dest, const char *src, size_t n) \
+ { \
+ size_t len = strlen (src); \
+ while (n-- && (*dest++ = (fn) (*src++))) \
+ ; \
+ return len; \
+ }
+
static int
tolower_ascii (int c)
{
return c >= 'A' && c <= 'Z' ? c + ('a' - 'A') : c;
}
-static size_t
-tolower_ascii_strxfrm (char *dest, const char *src, size_t n)
+static int
+toupper_ascii (int c)
{
- size_t len = strlen (src);
- while (n-- && (*dest++ = tolower_ascii (*src++)))
- ;
- return len;
+ return c >= 'A' && c <= 'Z' ? c : c - ('a' - 'A');
}
+TRIVIAL_STRXFRM (tolower_ascii_strxfrm, tolower_ascii)
+TRIVIAL_STRXFRM (toupper_ascii_strxfrm, toupper_ascii)
+
static int
strcasecmp_ascii (const char *a, const char *b)
{
@@ -1816,6 +1841,17 @@ strcasecmp_ascii (const char *a, const char *b)
return 0;
}
+static int
+strncasecmp_ascii (const char *a, const char *b, size_t n)
+{
+ int x;
+ while (n-- && (*a || *b))
+ if ((x = tolower_ascii (*(const unsigned char *) a++)
+ - tolower_ascii (*(const unsigned char *) b++)))
+ return x;
+ return 0;
+}
+
static bool
isalpha_ascii (int c)
{
@@ -1835,12 +1871,6 @@ isalnum_ascii (int c)
return isalpha_ascii (c) || isdigit_ascii (c);
}
-static int
-toupper_ascii (int c)
-{
- return c >= 'A' && c <= 'Z' ? c : c - ('a' - 'A');
-}
-
static bool
isspace_ascii (int c)
{
@@ -2070,7 +2100,7 @@ base64_encode (const void *data, size_t len, struct str *output)
// --- Utilities ---------------------------------------------------------------
static void
-split_str_ignore_empty (const char *s, char delimiter, struct str_vector *out)
+cstr_split_ignore_empty (const char *s, char delimiter, struct str_vector *out)
{
const char *begin = s, *end;
@@ -2086,7 +2116,7 @@ split_str_ignore_empty (const char *s, char delimiter, struct str_vector *out)
}
static char *
-strip_str_in_place (char *s, const char *stripped_chars)
+cstr_strip_in_place (char *s, const char *stripped_chars)
{
char *end = s + strlen (s);
while (end > s && strchr (stripped_chars, end[-1]))
@@ -2098,6 +2128,21 @@ strip_str_in_place (char *s, const char *stripped_chars)
return s;
}
+static void
+cstr_transform (char *s, int (*tolower) (int c))
+{
+ for (; *s; s++)
+ *s = tolower (*s);
+}
+
+static char *
+cstr_cut_until (const char *s, const char *alphabet)
+{
+ return xstrndup (s, strcspn (s, alphabet));
+}
+
+// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
+
static char *
join_str_vector (const struct str_vector *v, char delimiter)
{
@@ -2157,122 +2202,63 @@ iconv_xstrdup (iconv_t conv, char *in, size_t in_len, size_t *out_len)
}
static bool
-str_append_env_path (struct str *output, const char *var, bool only_absolute)
+set_boolean_if_valid (bool *out, const char *s)
{
- const char *value = getenv (var);
+ if (!strcasecmp (s, "yes")) *out = true;
+ else if (!strcasecmp (s, "no")) *out = false;
+ else if (!strcasecmp (s, "on")) *out = true;
+ else if (!strcasecmp (s, "off")) *out = false;
+ else if (!strcasecmp (s, "true")) *out = true;
+ else if (!strcasecmp (s, "false")) *out = false;
+ else return false;
- if (!value || (only_absolute && *value != '/'))
- return false;
-
- str_append (output, value);
return true;
}
-static void
-get_xdg_home_dir (struct str *output, const char *var, const char *def)
+static bool
+xstrtoul (unsigned long *out, const char *s, int base)
{
- str_reset (output);
- if (!str_append_env_path (output, var, true))
- {
- str_append_env_path (output, "HOME", false);
- str_append_c (output, '/');
- str_append (output, def);
- }
+ char *end;
+ errno = 0;
+ *out = strtoul (s, &end, base);
+ return errno == 0 && !*end && end != s;
}
-static void
-get_xdg_config_dirs (struct str_vector *out)
+static bool
+read_line (FILE *fp, struct str *s)
{
- struct str config_home;
- str_init (&config_home);
- get_xdg_home_dir (&config_home, "XDG_CONFIG_HOME", ".config");
- str_vector_add (out, config_home.str);
- str_free (&config_home);
+ int c;
+ bool at_end = true;
- const char *xdg_config_dirs;
- if ((xdg_config_dirs = getenv ("XDG_CONFIG_DIRS")))
- split_str_ignore_empty (xdg_config_dirs, ':', out);
-}
-
-static char *
-try_expand_tilde (const char *filename)
-{
- size_t until_slash = strcspn (filename, "/");
- if (!until_slash)
+ str_reset (s);
+ while ((c = fgetc (fp)) != EOF)
{
- struct str expanded;
- str_init (&expanded);
- str_append_env_path (&expanded, "HOME", false);
- str_append (&expanded, filename);
- return str_steal (&expanded);
- }
-
- int buf_len = sysconf (_SC_GETPW_R_SIZE_MAX);
- if (buf_len < 0)
- buf_len = 1024;
- struct passwd pwd, *success = NULL;
-
- char *user = xstrndup (filename, until_slash);
- char *buf = xmalloc (buf_len);
- while (getpwnam_r (user, &pwd, buf, buf_len, &success) == ERANGE)
- buf = xrealloc (buf, buf_len <<= 1);
- free (user);
-
- char *result = NULL;
- if (success)
- result = xstrdup_printf ("%s%s", pwd.pw_dir, filename + until_slash);
- free (buf);
- return result;
-}
-
-static char *
-resolve_config_filename (const char *filename)
-{
- // Absolute path is absolute
- if (*filename == '/')
- return xstrdup (filename);
-
- // We don't want to use wordexp() for this as it may execute /bin/sh
- if (*filename == '~')
- {
- // Paths to home directories ought to be absolute
- char *expanded = try_expand_tilde (filename + 1);
- if (expanded)
- return expanded;
- print_debug ("failed to expand the home directory in `%s'", filename);
- }
-
- struct str_vector paths;
- str_vector_init (&paths);
- get_xdg_config_dirs (&paths);
-
- struct str file;
- str_init (&file);
-
- char *result = NULL;
- for (unsigned i = 0; i < paths.len; i++)
- {
- // As per spec, relative paths are ignored
- if (*paths.vector[i] != '/')
+ at_end = false;
+ if (c == '\r')
continue;
-
- str_reset (&file);
- str_append_printf (&file, "%s/" PROGRAM_NAME "/%s",
- paths.vector[i], filename);
-
- struct stat st;
- if (!stat (file.str, &st))
- {
- result = str_steal (&file);
+ if (c == '\n')
break;
- }
+ str_append_c (s, c);
}
- str_vector_free (&paths);
- str_free (&file);
- return result;
+ return !at_end;
}
+static char *
+format_host_port_pair (const char *host, const char *port)
+{
+ // For when binding to the NULL address; would an asterisk be better?
+ if (!host)
+ host = "";
+
+ // IPv6 addresses mess with the "colon notation"; let's go with RFC 2732
+ if (strchr (host, ':'))
+ return xstrdup_printf ("[%s]:%s", host, port);
+ return xstrdup_printf ("%s:%s", host, port);
+}
+
+// --- File system -------------------------------------------------------------
+
static bool
ensure_directory_existence (const char *path, struct error **e)
{
@@ -2319,55 +2305,150 @@ mkdir_with_parents (char *path, struct error **e)
}
static bool
-set_boolean_if_valid (bool *out, const char *s)
+str_append_env_path (struct str *output, const char *var, bool only_absolute)
{
- if (!strcasecmp (s, "yes")) *out = true;
- else if (!strcasecmp (s, "no")) *out = false;
- else if (!strcasecmp (s, "on")) *out = true;
- else if (!strcasecmp (s, "off")) *out = false;
- else if (!strcasecmp (s, "true")) *out = true;
- else if (!strcasecmp (s, "false")) *out = false;
- else return false;
+ const char *value = getenv (var);
+ if (!value || (only_absolute && *value != '/'))
+ return false;
+
+ str_append (output, value);
return true;
}
-static bool
-xstrtoul (unsigned long *out, const char *s, int base)
+static void
+get_xdg_home_dir (struct str *output, const char *var, const char *def)
{
- char *end;
- errno = 0;
- *out = strtoul (s, &end, base);
- return errno == 0 && !*end && end != s;
+ str_reset (output);
+ if (!str_append_env_path (output, var, true))
+ {
+ str_append_env_path (output, "HOME", false);
+ str_append_c (output, '/');
+ str_append (output, def);
+ }
}
-static bool
-read_line (FILE *fp, struct str *s)
+static void
+get_xdg_config_dirs (struct str_vector *out)
{
- int c;
- bool at_end = true;
+ struct str config_home;
+ str_init (&config_home);
+ get_xdg_home_dir (&config_home, "XDG_CONFIG_HOME", ".config");
+ str_vector_add (out, config_home.str);
+ str_free (&config_home);
- str_reset (s);
- while ((c = fgetc (fp)) != EOF)
- {
- at_end = false;
- if (c == '\r')
- continue;
- if (c == '\n')
- break;
- str_append_c (s, c);
- }
-
- return !at_end;
+ const char *xdg_config_dirs;
+ if ((xdg_config_dirs = getenv ("XDG_CONFIG_DIRS")))
+ cstr_split_ignore_empty (xdg_config_dirs, ':', out);
}
static char *
-format_host_port_pair (const char *host, const char *port)
+try_expand_tilde (const char *filename)
{
- // IPv6 addresses mess with the "colon notation"; let's go with RFC 2732
- if (strchr (host, ':'))
- return xstrdup_printf ("[%s]:%s", host, port);
- return xstrdup_printf ("%s:%s", host, port);
+ size_t until_slash = strcspn (filename, "/");
+ if (!until_slash)
+ {
+ struct str expanded;
+ str_init (&expanded);
+ str_append_env_path (&expanded, "HOME", false);
+ str_append (&expanded, filename);
+ return str_steal (&expanded);
+ }
+
+ int buf_len = sysconf (_SC_GETPW_R_SIZE_MAX);
+ if (buf_len < 0)
+ buf_len = 1024;
+ struct passwd pwd, *success = NULL;
+
+ char *user = xstrndup (filename, until_slash);
+ char *buf = xmalloc (buf_len);
+ while (getpwnam_r (user, &pwd, buf, buf_len, &success) == ERANGE)
+ buf = xrealloc (buf, buf_len <<= 1);
+ free (user);
+
+ char *result = NULL;
+ if (success)
+ result = xstrdup_printf ("%s%s", pwd.pw_dir, filename + until_slash);
+ free (buf);
+ return result;
+}
+
+static char *
+resolve_relative_config_filename (const char *filename)
+{
+ struct str_vector paths;
+ str_vector_init (&paths);
+ get_xdg_config_dirs (&paths);
+
+ struct str file;
+ str_init (&file);
+
+ char *result = NULL;
+ for (unsigned i = 0; i < paths.len; i++)
+ {
+ // As per spec, relative paths are ignored
+ if (*paths.vector[i] != '/')
+ continue;
+
+ str_reset (&file);
+ str_append_printf (&file, "%s/" PROGRAM_NAME "/%s",
+ paths.vector[i], filename);
+
+ struct stat st;
+ if (!stat (file.str, &st))
+ {
+ result = str_steal (&file);
+ break;
+ }
+ }
+
+ str_vector_free (&paths);
+ str_free (&file);
+ return result;
+}
+
+static char *
+resolve_relative_runtime_filename (const char *filename)
+{
+ struct str path;
+ str_init (&path);
+
+ const char *runtime_dir = getenv ("XDG_RUNTIME_DIR");
+ if (runtime_dir && *runtime_dir == '/')
+ str_append (&path, runtime_dir);
+ else
+ get_xdg_home_dir (&path, "XDG_DATA_HOME", ".local/share");
+ str_append_printf (&path, "/%s/%s", PROGRAM_NAME, filename);
+
+ // Try to create the file's ancestors;
+ // typically the user will want to immediately create a file in there
+ const char *last_slash = strrchr (path.str, '/');
+ if (last_slash && last_slash != path.str)
+ {
+ char *copy = xstrndup (path.str, last_slash - path.str);
+ (void) mkdir_with_parents (copy, NULL);
+ free (copy);
+ }
+ return str_steal (&path);
+}
+
+static char *
+resolve_filename (const char *filename, char *(*relative_cb) (const char *))
+{
+ // Absolute path is absolute
+ if (*filename == '/')
+ return xstrdup (filename);
+
+ // We don't want to use wordexp() for this as it may execute /bin/sh
+ if (*filename == '~')
+ {
+ // Paths to home directories ought to be absolute
+ char *expanded = try_expand_tilde (filename + 1);
+ if (expanded)
+ return expanded;
+ print_debug ("failed to expand the home directory in `%s'", filename);
+ }
+ return relative_cb (filename);
}
// --- OpenSSL -----------------------------------------------------------------
@@ -2488,7 +2569,8 @@ load_config_defaults (struct str_map *config, const struct config_item *table)
static bool
read_config_file (struct str_map *config, struct error **e)
{
- char *filename = resolve_config_filename (PROGRAM_NAME ".conf");
+ char *filename = resolve_filename
+ (PROGRAM_NAME ".conf", resolve_relative_config_filename);
if (!filename)
return true;