Import stuff from uirc3

This commit is contained in:
Přemysl Eric Janouch 2015-07-11 17:44:37 +02:00
parent 34569e2b62
commit 7fa873fb96
2 changed files with 241 additions and 156 deletions

View File

@ -66,7 +66,7 @@ irc_parse_message_tags (const char *tags, struct str_map *out)
{ {
struct str_vector v; struct str_vector v;
str_vector_init (&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++) 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; return c >= 'A' && c <= 'Z' ? c + ('a' - 'A') : c;
} }
static size_t static int
irc_strxfrm (char *dest, const char *src, size_t n) irc_tolower_strict (int c)
{ {
size_t len = strlen (src); if (c == '[') return '{';
while (n-- && (*dest++ = irc_tolower (*src++))) if (c == ']') return '}';
; if (c == '\\') return '|';
return len; 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 static int
irc_strcmp (const char *a, const char *b) irc_strcmp (const char *a, const char *b)
{ {

380
liberty.c
View File

@ -314,6 +314,15 @@ xstrndup (const char *s, size_t n)
(tail) = (link); \ (tail) = (link); \
BLOCK_END 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) \ #define LIST_UNLINK_WITH_TAIL(head, tail, link) \
BLOCK_START \ BLOCK_START \
if ((tail) == (link)) \ if ((tail) == (link)) \
@ -1468,6 +1477,12 @@ poller_timer_set (struct poller_timer *self, int timeout_ms)
poller_timers_set (self->timers, self); poller_timers_set (self->timers, self);
} }
static bool
poller_timer_is_active (struct poller_timer *self)
{
return self->index != -1;
}
static void static void
poller_timer_reset (struct poller_timer *self) poller_timer_reset (struct poller_timer *self)
{ {
@ -1790,21 +1805,31 @@ msg_writer_flush (struct msg_writer *self, size_t *len)
// --- ASCII ------------------------------------------------------------------- // --- 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 static int
tolower_ascii (int c) tolower_ascii (int c)
{ {
return c >= 'A' && c <= 'Z' ? c + ('a' - 'A') : c; return c >= 'A' && c <= 'Z' ? c + ('a' - 'A') : c;
} }
static size_t static int
tolower_ascii_strxfrm (char *dest, const char *src, size_t n) toupper_ascii (int c)
{ {
size_t len = strlen (src); return c >= 'A' && c <= 'Z' ? c : c - ('a' - 'A');
while (n-- && (*dest++ = tolower_ascii (*src++)))
;
return len;
} }
TRIVIAL_STRXFRM (tolower_ascii_strxfrm, tolower_ascii)
TRIVIAL_STRXFRM (toupper_ascii_strxfrm, toupper_ascii)
static int static int
strcasecmp_ascii (const char *a, const char *b) strcasecmp_ascii (const char *a, const char *b)
{ {
@ -1816,6 +1841,17 @@ strcasecmp_ascii (const char *a, const char *b)
return 0; 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 static bool
isalpha_ascii (int c) isalpha_ascii (int c)
{ {
@ -1835,12 +1871,6 @@ isalnum_ascii (int c)
return isalpha_ascii (c) || isdigit_ascii (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 static bool
isspace_ascii (int c) isspace_ascii (int c)
{ {
@ -2070,7 +2100,7 @@ base64_encode (const void *data, size_t len, struct str *output)
// --- Utilities --------------------------------------------------------------- // --- Utilities ---------------------------------------------------------------
static void 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; const char *begin = s, *end;
@ -2086,7 +2116,7 @@ split_str_ignore_empty (const char *s, char delimiter, struct str_vector *out)
} }
static char * 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); char *end = s + strlen (s);
while (end > s && strchr (stripped_chars, end[-1])) while (end > s && strchr (stripped_chars, end[-1]))
@ -2098,6 +2128,21 @@ strip_str_in_place (char *s, const char *stripped_chars)
return s; 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 * static char *
join_str_vector (const struct str_vector *v, char delimiter) 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 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; return true;
} }
static void static bool
get_xdg_home_dir (struct str *output, const char *var, const char *def) xstrtoul (unsigned long *out, const char *s, int base)
{ {
str_reset (output); char *end;
if (!str_append_env_path (output, var, true)) errno = 0;
{ *out = strtoul (s, &end, base);
str_append_env_path (output, "HOME", false); return errno == 0 && !*end && end != s;
str_append_c (output, '/');
str_append (output, def);
}
} }
static void static bool
get_xdg_config_dirs (struct str_vector *out) read_line (FILE *fp, struct str *s)
{ {
struct str config_home; int c;
str_init (&config_home); bool at_end = true;
get_xdg_home_dir (&config_home, "XDG_CONFIG_HOME", ".config");
str_vector_add (out, config_home.str);
str_free (&config_home);
const char *xdg_config_dirs; str_reset (s);
if ((xdg_config_dirs = getenv ("XDG_CONFIG_DIRS"))) while ((c = fgetc (fp)) != EOF)
split_str_ignore_empty (xdg_config_dirs, ':', out);
}
static char *
try_expand_tilde (const char *filename)
{ {
size_t until_slash = strcspn (filename, "/"); at_end = false;
if (!until_slash) if (c == '\r')
{
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] != '/')
continue; continue;
if (c == '\n')
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; break;
} str_append_c (s, c);
} }
str_vector_free (&paths); return !at_end;
str_free (&file);
return result;
} }
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 static bool
ensure_directory_existence (const char *path, struct error **e) ensure_directory_existence (const char *path, struct error **e)
{ {
@ -2319,55 +2305,150 @@ mkdir_with_parents (char *path, struct error **e)
} }
static bool 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; const char *value = getenv (var);
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; return true;
} }
static bool static void
xstrtoul (unsigned long *out, const char *s, int base) get_xdg_home_dir (struct str *output, const char *var, const char *def)
{ {
char *end; str_reset (output);
errno = 0; if (!str_append_env_path (output, var, true))
*out = strtoul (s, &end, base); {
return errno == 0 && !*end && end != s; str_append_env_path (output, "HOME", false);
str_append_c (output, '/');
str_append (output, def);
}
} }
static bool static void
read_line (FILE *fp, struct str *s) get_xdg_config_dirs (struct str_vector *out)
{ {
int c; struct str config_home;
bool at_end = true; 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); const char *xdg_config_dirs;
while ((c = fgetc (fp)) != EOF) if ((xdg_config_dirs = getenv ("XDG_CONFIG_DIRS")))
{ cstr_split_ignore_empty (xdg_config_dirs, ':', out);
at_end = false;
if (c == '\r')
continue;
if (c == '\n')
break;
str_append_c (s, c);
}
return !at_end;
} }
static char * 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 size_t until_slash = strcspn (filename, "/");
if (strchr (host, ':')) if (!until_slash)
return xstrdup_printf ("[%s]:%s", host, port); {
return xstrdup_printf ("%s:%s", host, port); 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 ----------------------------------------------------------------- // --- OpenSSL -----------------------------------------------------------------
@ -2488,7 +2569,8 @@ load_config_defaults (struct str_map *config, const struct config_item *table)
static bool static bool
read_config_file (struct str_map *config, struct error **e) 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) if (!filename)
return true; return true;