From 7fa873fb964156e71a00174f50e3f4bc343bcc04 Mon Sep 17 00:00:00 2001 From: Přemysl Janouch Date: Sat, 11 Jul 2015 17:44:37 +0200 Subject: Import stuff from uirc3 --- liberty-proto.c | 17 +-- liberty.c | 312 +++++++++++++++++++++++++++++++++++--------------------- 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) { @@ -2156,6 +2201,109 @@ iconv_xstrdup (iconv_t conv, char *in, size_t in_len, size_t *out_len) return buf; } +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) { @@ -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; -- cgit v1.2.3-70-g09d2