aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--liberty-proto.c17
-rw-r--r--liberty.c312
2 files changed, 207 insertions, 122 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,6 +2202,109 @@ iconv_xstrdup (iconv_t conv, char *in, size_t in_len, size_t *out_len)
}
static bool
+set_boolean_if_valid (bool *out, const char *s)
+{
+ 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;
+
+ return true;
+}
+
+static bool
+xstrtoul (unsigned long *out, const char *s, int base)
+{
+ char *end;
+ errno = 0;
+ *out = strtoul (s, &end, base);
+ return errno == 0 && !*end && end != s;
+}
+
+static bool
+read_line (FILE *fp, struct str *s)
+{
+ int c;
+ bool at_end = true;
+
+ 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;
+}
+
+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)
+{
+ struct stat st;
+
+ if (stat (path, &st))
+ {
+ if (mkdir (path, S_IRWXU | S_IRWXG | S_IRWXO))
+ {
+ error_set (e, "cannot create directory `%s': %s",
+ path, strerror (errno));
+ return false;
+ }
+ }
+ else if (!S_ISDIR (st.st_mode))
+ {
+ error_set (e, "cannot create directory `%s': %s",
+ path, "file exists but is not a directory");
+ return false;
+ }
+ return true;
+}
+
+static bool
+mkdir_with_parents (char *path, struct error **e)
+{
+ char *p = path;
+
+ // XXX: This is prone to the TOCTTOU problem. The solution would be to
+ // rewrite the function using the {mkdir,fstat}at() functions from
+ // POSIX.1-2008, ideally returning a file descriptor to the open
+ // directory, with the current code as a fallback. Or to use chdir().
+ while ((p = strchr (p + 1, '/')))
+ {
+ *p = '\0';
+ bool success = ensure_directory_existence (path, e);
+ *p = '/';
+
+ if (!success)
+ return false;
+ }
+
+ return ensure_directory_existence (path, e);
+}
+
+static bool
str_append_env_path (struct str *output, const char *var, bool only_absolute)
{
const char *value = getenv (var);
@@ -2191,7 +2339,7 @@ get_xdg_config_dirs (struct str_vector *out)
const char *xdg_config_dirs;
if ((xdg_config_dirs = getenv ("XDG_CONFIG_DIRS")))
- split_str_ignore_empty (xdg_config_dirs, ':', out);
+ cstr_split_ignore_empty (xdg_config_dirs, ':', out);
}
static char *
@@ -2226,22 +2374,8 @@ try_expand_tilde (const char *filename)
}
static char *
-resolve_config_filename (const char *filename)
+resolve_relative_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);
@@ -2273,101 +2407,48 @@ resolve_config_filename (const char *filename)
return result;
}
-static bool
-ensure_directory_existence (const char *path, struct error **e)
+static char *
+resolve_relative_runtime_filename (const char *filename)
{
- struct stat st;
-
- if (stat (path, &st))
- {
- if (mkdir (path, S_IRWXU | S_IRWXG | S_IRWXO))
- {
- error_set (e, "cannot create directory `%s': %s",
- path, strerror (errno));
- return false;
- }
- }
- else if (!S_ISDIR (st.st_mode))
- {
- error_set (e, "cannot create directory `%s': %s",
- path, "file exists but is not a directory");
- return false;
- }
- return true;
-}
+ struct str path;
+ str_init (&path);
-static bool
-mkdir_with_parents (char *path, struct error **e)
-{
- char *p = 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);
- // XXX: This is prone to the TOCTTOU problem. The solution would be to
- // rewrite the function using the {mkdir,fstat}at() functions from
- // POSIX.1-2008, ideally returning a file descriptor to the open
- // directory, with the current code as a fallback. Or to use chdir().
- while ((p = strchr (p + 1, '/')))
+ // 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)
{
- *p = '\0';
- bool success = ensure_directory_existence (path, e);
- *p = '/';
-
- if (!success)
- return false;
+ char *copy = xstrndup (path.str, last_slash - path.str);
+ (void) mkdir_with_parents (copy, NULL);
+ free (copy);
}
-
- return ensure_directory_existence (path, e);
-}
-
-static bool
-set_boolean_if_valid (bool *out, const char *s)
-{
- 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;
-
- return true;
-}
-
-static bool
-xstrtoul (unsigned long *out, const char *s, int base)
-{
- char *end;
- errno = 0;
- *out = strtoul (s, &end, base);
- return errno == 0 && !*end && end != s;
+ return str_steal (&path);
}
-static bool
-read_line (FILE *fp, struct str *s)
+static char *
+resolve_filename (const char *filename, char *(*relative_cb) (const char *))
{
- int c;
- bool at_end = true;
+ // Absolute path is absolute
+ if (*filename == '/')
+ return xstrdup (filename);
- str_reset (s);
- while ((c = fgetc (fp)) != EOF)
+ // We don't want to use wordexp() for this as it may execute /bin/sh
+ if (*filename == '~')
{
- at_end = false;
- if (c == '\r')
- continue;
- if (c == '\n')
- break;
- str_append_c (s, c);
+ // 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 !at_end;
-}
-
-static char *
-format_host_port_pair (const char *host, const char *port)
-{
- // 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);
+ 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;