diff --git a/src/miraclectl.c b/src/miraclectl.c index ab2e80c..130381a 100644 --- a/src/miraclectl.c +++ b/src/miraclectl.c @@ -90,7 +90,7 @@ static bool is_cli(void) static void cli_printv(const char *fmt, va_list args) { SHL_PROTECT_ERRNO; - _shl_cleanup_free_ char *line = NULL; + _shl_free_ char *line = NULL; int point; bool async; @@ -281,8 +281,8 @@ static int cli_do(char **args, unsigned int n) static void cli_handler_fn(char *input) { - _shl_cleanup_free_ char *original = input; - _shl_cleanup_strv_ char **args = NULL; + _shl_free_ char *original = input; + _shl_strv_free_ char **args = NULL; int r; if (!input) { @@ -529,7 +529,7 @@ static int cmd_list_link(sd_bus_message *m, const char *link) static int cmd_list_links(sd_bus_message *m) { - _shl_cleanup_free_ char *link = NULL; + _shl_free_ char *link = NULL; unsigned int link_cnt = 0; const char *obj; int r; @@ -588,7 +588,7 @@ static int cmd_list_peer(sd_bus_message *m, const char *link_filter, const char *peer) { - _shl_cleanup_free_ char *link = NULL; + _shl_free_ char *link = NULL; const char *obj, *name = ""; int r, connected = 0; @@ -686,7 +686,7 @@ static int cmd_list_peer(sd_bus_message *m, static int cmd_list_peers(sd_bus_message *m, const char *link_filter) { - _shl_cleanup_free_ char *peer = NULL; + _shl_free_ char *peer = NULL; unsigned int peer_cnt = 0; const char *obj; int r; @@ -793,7 +793,7 @@ static int cmd_list(char **args, unsigned int n) static int cmd_select(char **args, unsigned int n) { _cleanup_sd_bus_error_ sd_bus_error err = SD_BUS_ERROR_NULL; - _shl_cleanup_free_ char *path = NULL, *name = NULL; + _shl_free_ char *path = NULL, *name = NULL; int r; if (!n) { @@ -845,8 +845,8 @@ static int cmd_show_link(char **args, unsigned int n) { _cleanup_sd_bus_error_ sd_bus_error err = SD_BUS_ERROR_NULL; _cleanup_sd_bus_message_ sd_bus_message *m = NULL; - _shl_cleanup_free_ char *path = NULL, *name = NULL; - _shl_cleanup_free_ char *type = NULL, *iface = NULL, *fname = NULL; + _shl_free_ char *path = NULL, *name = NULL; + _shl_free_ char *type = NULL, *iface = NULL, *fname = NULL; const char *t, *arg_link; int r; @@ -949,9 +949,9 @@ static int cmd_show_peer(char **args, unsigned int n) { _cleanup_sd_bus_error_ sd_bus_error err = SD_BUS_ERROR_NULL; _cleanup_sd_bus_message_ sd_bus_message *m = NULL; - _shl_cleanup_free_ char *path = NULL, *name = NULL; - _shl_cleanup_free_ char *link = NULL, *fname = NULL, *iface = NULL; - _shl_cleanup_free_ char *laddr = NULL, *raddr = NULL; + _shl_free_ char *path = NULL, *name = NULL; + _shl_free_ char *link = NULL, *fname = NULL, *iface = NULL; + _shl_free_ char *laddr = NULL, *raddr = NULL; const char *t; int r, is_connected = false; @@ -1081,7 +1081,7 @@ static int cmd_add_link(char **args, unsigned int n) { _cleanup_sd_bus_error_ sd_bus_error err = SD_BUS_ERROR_NULL; _cleanup_sd_bus_message_ sd_bus_message *m = NULL; - _shl_cleanup_free_ char *link = NULL, *type = NULL; + _shl_free_ char *link = NULL, *type = NULL; const char *name; char *t, *iface; int r; @@ -1158,7 +1158,7 @@ static int cmd_set_link_name(char **args, unsigned int n) { _cleanup_sd_bus_error_ sd_bus_error err = SD_BUS_ERROR_NULL; _cleanup_sd_bus_message_ sd_bus_message *m = NULL; - _shl_cleanup_free_ char *path = NULL, *name = NULL; + _shl_free_ char *path = NULL, *name = NULL; const char *arg_link, *arg_name; int r; @@ -1224,7 +1224,7 @@ static int cmd_set_link_name(char **args, unsigned int n) static int cmd_start_scan(char **args, unsigned int n) { _cleanup_sd_bus_error_ sd_bus_error err = SD_BUS_ERROR_NULL; - _shl_cleanup_free_ char *path = NULL, *name = NULL; + _shl_free_ char *path = NULL, *name = NULL; const char *arg_link; int r; @@ -1266,7 +1266,7 @@ static int cmd_start_scan(char **args, unsigned int n) static int cmd_stop_scan(char **args, unsigned int n) { _cleanup_sd_bus_error_ sd_bus_error err = SD_BUS_ERROR_NULL; - _shl_cleanup_free_ char *path = NULL, *name = NULL; + _shl_free_ char *path = NULL, *name = NULL; const char *arg_link; int r; @@ -1310,7 +1310,7 @@ static char *scan_link; static int cmd_scan_stop(bool async) { _cleanup_sd_bus_error_ sd_bus_error err = SD_BUS_ERROR_NULL; - _shl_cleanup_free_ char *path = NULL, *name = NULL; + _shl_free_ char *path = NULL, *name = NULL; int r; if (!scan_link) @@ -1372,7 +1372,7 @@ static void cmd_scan_list(void) static int cmd_scan(char **args, unsigned int n) { _cleanup_sd_bus_error_ sd_bus_error err = SD_BUS_ERROR_NULL; - _shl_cleanup_free_ char *path = NULL, *name = NULL; + _shl_free_ char *path = NULL, *name = NULL; const char *arg_link; int r; @@ -1429,7 +1429,7 @@ static int cmd_scan(char **args, unsigned int n) static int cmd_allow(char **args, unsigned int n) { _cleanup_sd_bus_error_ sd_bus_error err = SD_BUS_ERROR_NULL; - _shl_cleanup_free_ char *name = NULL, *path = NULL; + _shl_free_ char *name = NULL, *path = NULL; const char *pin = ""; int r; @@ -1470,7 +1470,7 @@ static int cmd_allow(char **args, unsigned int n) static int cmd_reject(char **args, unsigned int n) { _cleanup_sd_bus_error_ sd_bus_error err = SD_BUS_ERROR_NULL; - _shl_cleanup_free_ char *name = NULL, *path = NULL; + _shl_free_ char *name = NULL, *path = NULL; int r; name = bus_label_escape(args[0]); @@ -1507,7 +1507,7 @@ static int cmd_reject(char **args, unsigned int n) static int cmd_connect(char **args, unsigned int n) { _cleanup_sd_bus_error_ sd_bus_error err = SD_BUS_ERROR_NULL; - _shl_cleanup_free_ char *name = NULL, *path = NULL; + _shl_free_ char *name = NULL, *path = NULL; const char *peer, *prov, *pin; int r; @@ -1549,7 +1549,7 @@ static int cmd_connect(char **args, unsigned int n) static int cmd_disconnect(char **args, unsigned int n) { _cleanup_sd_bus_error_ sd_bus_error err = SD_BUS_ERROR_NULL; - _shl_cleanup_free_ char *name = NULL, *path = NULL; + _shl_free_ char *name = NULL, *path = NULL; int r; name = bus_label_escape(args[0]); @@ -1597,8 +1597,8 @@ static int filters_show_peer(const char *peer) { _cleanup_sd_bus_error_ sd_bus_error err = SD_BUS_ERROR_NULL; _cleanup_sd_bus_message_ sd_bus_message *m = NULL; - _shl_cleanup_free_ char *path = NULL, *name = NULL; - _shl_cleanup_free_ char *link = NULL, *fname = NULL; + _shl_free_ char *path = NULL, *name = NULL; + _shl_free_ char *link = NULL, *fname = NULL; const char *t; int r; @@ -1682,7 +1682,7 @@ static int filters_show_link(const char *link) { _cleanup_sd_bus_error_ sd_bus_error err = SD_BUS_ERROR_NULL; _cleanup_sd_bus_message_ sd_bus_message *m = NULL; - _shl_cleanup_free_ char *path = NULL, *name = NULL, *fname = NULL; + _shl_free_ char *path = NULL, *name = NULL, *fname = NULL; const char *t; int r; @@ -1754,7 +1754,7 @@ static int filters_object_fn(sd_bus *bus, void *data, sd_bus_error *err) { - _shl_cleanup_free_ char *peer = NULL, *link = NULL; + _shl_free_ char *peer = NULL, *link = NULL; const char *obj, *t; bool added; int r; @@ -1849,7 +1849,7 @@ static int filters_props_fn(sd_bus *bus, void *data, sd_bus_error *err) { - _shl_cleanup_free_ char *peer = NULL, *link = NULL; + _shl_free_ char *peer = NULL, *link = NULL; const char *path, *t; int r; @@ -1919,7 +1919,7 @@ static int filters_peer_fn(sd_bus *bus, void *data, sd_bus_error *err) { - _shl_cleanup_free_ char *peer = NULL; + _shl_free_ char *peer = NULL; const char *path, *type, *pin; int r; @@ -1961,7 +1961,7 @@ static int filters_link_fn(sd_bus *bus, void *data, sd_bus_error *err) { - _shl_cleanup_free_ char *link = NULL; + _shl_free_ char *link = NULL; const char *path; path = sd_bus_message_get_path(m); diff --git a/src/miracled-dbus.c b/src/miracled-dbus.c index 049a8df..1de95f2 100644 --- a/src/miracled-dbus.c +++ b/src/miracled-dbus.c @@ -102,7 +102,7 @@ static int peer_dbus_get_link(sd_bus *bus, void *data, sd_bus_error *err) { - _cleanup_free_ char *link = NULL; + _shl_free_ char *link = NULL; struct peer *p = data; int r; @@ -298,7 +298,7 @@ void peer_dbus_provision_request(struct peer *p, const char *type, const char *pin) { - _cleanup_free_ char *path = NULL; + _shl_free_ char *path = NULL; int r; if (!type) @@ -321,7 +321,7 @@ void peer_dbus_provision_request(struct peer *p, void peer_dbus_properties_changed(struct peer *p, const char *prop, ...) { - _cleanup_free_ char *path = NULL; + _shl_free_ char *path = NULL; char **strv; int r; @@ -340,7 +340,7 @@ void peer_dbus_properties_changed(struct peer *p, const char *prop, ...) void peer_dbus_added(struct peer *p) { - _cleanup_free_ char *path = NULL; + _shl_free_ char *path = NULL; int r; path = shl_strcat("/org/freedesktop/miracle/peer/", p->name); @@ -361,7 +361,7 @@ void peer_dbus_added(struct peer *p) void peer_dbus_removed(struct peer *p) { - _cleanup_free_ char *path = NULL; + _shl_free_ char *path = NULL; int r; path = shl_strcat("/org/freedesktop/miracle/peer/", p->name); @@ -561,7 +561,7 @@ static int link_dbus_find(sd_bus *bus, void link_dbus_properties_changed(struct link *l, const char *prop, ...) { - _cleanup_free_ char *path = NULL; + _shl_free_ char *path = NULL; char **strv; int r; @@ -580,7 +580,7 @@ void link_dbus_properties_changed(struct link *l, const char *prop, ...) void link_dbus_scan_stopped(struct link *l) { - _cleanup_free_ char *path = NULL; + _shl_free_ char *path = NULL; int r; path = shl_strcat("/org/freedesktop/miracle/link/", l->name); @@ -598,7 +598,7 @@ void link_dbus_scan_stopped(struct link *l) void link_dbus_added(struct link *l) { - _cleanup_free_ char *path = NULL; + _shl_free_ char *path = NULL; int r; path = shl_strcat("/org/freedesktop/miracle/link/", l->name); @@ -619,7 +619,7 @@ void link_dbus_added(struct link *l) void link_dbus_removed(struct link *l) { - _cleanup_free_ char *path = NULL; + _shl_free_ char *path = NULL; int r; path = shl_strcat("/org/freedesktop/miracle/link/", l->name); @@ -675,7 +675,7 @@ static int manager_dbus_add_link(sd_bus *bus, sd_bus_message *msg, static int manager_dbus_remove_link(sd_bus *bus, sd_bus_message *msg, void *data, sd_bus_error *err) { - _cleanup_free_ char *link = NULL; + _shl_free_ char *link = NULL; struct manager *m = data; struct link *l; const char *name; diff --git a/src/miracled-link.c b/src/miracled-link.c index ccaa157..9c7e8b9 100644 --- a/src/miracled-link.c +++ b/src/miracled-link.c @@ -140,7 +140,7 @@ static void link_wifi_event_fn(struct wifi *w, void *data, static int link_wifi_start(struct link *l) { - _shl_cleanup_free_ char *path = NULL; + _shl_free_ char *path = NULL; struct wifi_dev *d; int r; @@ -209,7 +209,7 @@ static int link_wifi_startup_fn(sd_event_source *source, static int link_wifi_init(struct link *l) { - _shl_cleanup_free_ char *path = NULL; + _shl_free_ char *path = NULL; int r; r = wifi_new(l->m->event, link_wifi_event_fn, l, &l->w); diff --git a/src/miracled.c b/src/miracled.c index bb919d0..7a18f4e 100644 --- a/src/miracled.c +++ b/src/miracled.c @@ -104,7 +104,7 @@ static void manager_add_link_from_udev(struct manager *m, static void manager_remove_link_from_udev(struct manager *m, struct udev_device *d) { - _shl_cleanup_free_ char *name = NULL; + _shl_free_ char *name = NULL; int r; struct link *l; diff --git a/src/shared/shl_dlist.h b/src/shared/shl_dlist.h index 9d6f6fc..7cbf1e1 100644 --- a/src/shared/shl_dlist.h +++ b/src/shared/shl_dlist.h @@ -74,9 +74,11 @@ static inline void shl_dlist__unlink(struct shl_dlist *prev, static inline void shl_dlist_unlink(struct shl_dlist *e) { - shl_dlist__unlink(e->prev, e->next); - e->prev = NULL; - e->next = NULL; + if (e->prev && e->next) { + shl_dlist__unlink(e->prev, e->next); + e->prev = NULL; + e->next = NULL; + } } static inline bool shl_dlist_empty(struct shl_dlist *head) diff --git a/src/shared/shl_log.h b/src/shared/shl_log.h index ffeb1e4..ce660ce 100644 --- a/src/shared/shl_log.h +++ b/src/shared/shl_log.h @@ -22,14 +22,30 @@ #include enum log_severity { +#ifndef LOG_FATAL LOG_FATAL = 0, +#endif +#ifndef LOG_ALERT LOG_ALERT = 1, +#endif +#ifndef LOG_CRITICAL LOG_CRITICAL = 2, +#endif +#ifndef LOG_ERROR LOG_ERROR = 3, +#endif +#ifndef LOG_WARNING LOG_WARNING = 4, +#endif +#ifndef LOG_NOTICE LOG_NOTICE = 5, +#endif +#ifndef LOG_INFO LOG_INFO = 6, +#endif +#ifndef LOG_DEBUG LOG_DEBUG = 7, +#endif LOG_SEV_NUM, }; diff --git a/src/shared/shl_macro.h b/src/shared/shl_macro.h index 6864c61..1b7b341 100644 --- a/src/shared/shl_macro.h +++ b/src/shared/shl_macro.h @@ -48,7 +48,7 @@ static inline void shl_freep(void *p) free(*(void**)p); } -#define _shl_cleanup_free_ _shl_cleanup_(shl_freep) +#define _shl_free_ _shl_cleanup_(shl_freep) static inline void shl_set_errno(int *r) { diff --git a/src/shared/shl_util.c b/src/shared/shl_util.c index d9129c4..8508156 100644 --- a/src/shared/shl_util.c +++ b/src/shared/shl_util.c @@ -16,6 +16,10 @@ #include #include #include +#include +#include +#include +#include #include "shl_macro.h" #include "shl_util.h" @@ -260,6 +264,44 @@ void *shl_greedy_realloc0(void **mem, size_t *size, size_t need) return p; } +void *shl_greedy_realloc_t(void **arr, size_t *cnt, size_t need, size_t ts) +{ + size_t ncnt; + void *p; + + if (*cnt >= need) + return *arr; + if (!ts) + return NULL; + + ncnt = SHL_ALIGN_POWER2(shl_max_t(size_t, 64U, need)); + if (ncnt == 0) + return NULL; + + p = realloc(*arr, ncnt * ts); + if (!p) + return NULL; + + *arr = p; + *cnt = ncnt; + return p; +} + +void *shl_greedy_realloc0_t(void **arr, size_t *cnt, size_t need, size_t ts) +{ + size_t prev = *cnt; + uint8_t *p; + + p = shl_greedy_realloc_t(arr, cnt, need, ts); + if (!p) + return NULL; + + if (*cnt > prev) + shl_memzero(&p[prev * ts], (*cnt - prev) * ts); + + return p; +} + /* * String Helpers */ @@ -322,6 +364,102 @@ char *shl_strjoin(const char *first, ...) { return str; } +static int shl__split_push(char ***strv, + size_t *strv_num, + size_t *strv_size, + const char *str, + size_t len) +{ + size_t strv_need; + char *ns; + + strv_need = (*strv_num + 2) * sizeof(**strv); + if (!shl_greedy_realloc0((void**)strv, strv_size, strv_need)) + return -ENOMEM; + + ns = malloc(len + 1); + memcpy(ns, str, len); + ns[len] = 0; + + (*strv)[*strv_num] = ns; + *strv_num += 1; + + return 0; +} + +int shl_strsplit_n(const char *str, size_t len, const char *sep, char ***out) +{ + char **strv; + size_t i, j, strv_num, strv_size; + const char *pos; + int r; + + if (!out || !sep) + return -EINVAL; + if (!str) + str = ""; + + strv_num = 0; + strv_size = sizeof(*strv); + strv = malloc(strv_size); + if (!strv) + return -ENOMEM; + + pos = str; + + for (i = 0; i < len; ++i) { + for (j = 0; sep[j]; ++j) { + if (str[i] != sep[j]) + continue; + + /* ignore empty tokens */ + if (pos != &str[i]) { + r = shl__split_push(&strv, + &strv_num, + &strv_size, + pos, + &str[i] - pos); + if (r < 0) + goto error; + } + + pos = &str[i + 1]; + break; + } + } + + /* copy trailing token if available */ + if (i > 0 && pos != &str[i]) { + r = shl__split_push(&strv, + &strv_num, + &strv_size, + pos, + &str[i] - pos); + if (r < 0) + goto error; + } + + if ((int)strv_num < (ssize_t)strv_num) { + r = -ENOMEM; + goto error; + } + + strv[strv_num] = NULL; + *out = strv; + return strv_num; + +error: + for (i = 0; i < strv_num; ++i) + free(strv[i]); + free(strv); + return r; +} + +int shl_strsplit(const char *str, const char *sep, char ***out) +{ + return shl_strsplit_n(str, str ? strlen(str) : 0, sep, out); +} + /* * strv */ @@ -530,3 +668,258 @@ int shl_qstr_tokenize(const char *str, char ***out) { return shl_qstr_tokenize_n(str, str ? strlen(str) : 0, out); } + +size_t shl__qstr_encode(char *dst, const char *src, bool need_quote) +{ + size_t l = 0; + + if (need_quote) + dst[l++] = '"'; + + for ( ; *src; ++src) { + switch (*src) { + case '\\': + case '\"': + dst[l++] = '\\'; + dst[l++] = *src; + break; + default: + dst[l++] = *src; + break; + } + } + + if (need_quote) + dst[l++] = '"'; + + return l; +} + +size_t shl__qstr_length(const char *str, bool *need_quote) +{ + size_t l = 0; + + *need_quote = false; + + do { + switch (*str++) { + case 0: + return l; + case ' ': + case '\t': + case '\n': + case '\v': + *need_quote = true; + } + } while (++l); + + return l - 1; +} + +int shl_qstr_join(char **strv, char **out) +{ + _shl_free_ char *line = NULL; + size_t len, size, l, need; + bool need_quote; + + len = 0; + size = 0; + + if (!SHL_GREEDY_REALLOC_T(line, size, 1)) + return -ENOMEM; + + *line = 0; + + for ( ; *strv; ++strv) { + l = shl__qstr_length(*strv, &need_quote); + + /* at most 2 byte per char (escapes) */ + if (l * 2 < l) + return -ENOMEM; + need = l * 2; + + /* on top of current length */ + if (need + len < need) + return -ENOMEM; + need += len; + + /* at most 4 extra chars: 2 quotes + 0 + separator */ + if (need + 4 < len) + return -ENOMEM; + need += 4; + + /* make sure line is big enough */ + if (!SHL_GREEDY_REALLOC_T(line, size, need)) + return -ENOMEM; + + if (len) + line[len++] = ' '; + + len += shl__qstr_encode(line + len, *strv, need_quote); + } + + if ((size_t)(int)len != len) + return -ENOMEM; + + line[len] = 0; + *out = line; + line = NULL; + return len; +} + +/* + * mkdir + */ + +static int shl__is_dir(const char *path) +{ + struct stat st; + + if (stat(path, &st) < 0) + return -errno; + + return S_ISDIR(st.st_mode); +} + +const char *shl__path_startswith(const char *path, const char *prefix) +{ + size_t pathl, prefixl; + + if (!path) + return NULL; + if (!prefix) + return path; + + if ((path[0] == '/') != (prefix[0] == '/')) + return NULL; + + /* compare all components */ + while (true) { + path += strspn(path, "/"); + prefix += strspn(prefix, "/"); + + if (*prefix == 0) + return (char*)path; + if (*path == 0) + return NULL; + + pathl = strcspn(path, "/"); + prefixl = strcspn(prefix, "/"); + if (pathl != prefixl || memcmp(path, prefix, pathl)) + return NULL; + + path += pathl; + prefix += prefixl; + } +} + +int shl__mkdir_parents(const char *prefix, const char *path, mode_t mode) +{ + const char *p, *e; + char *t; + int r; + + if (!shl__path_startswith(path, prefix)) + return -ENOTDIR; + + e = strrchr(path, '/'); + if (!e || e == path) + return 0; + + p = strndupa(path, e - path); + r = shl__is_dir(p); + if (r > 0) + return 0; + if (r == 0) + return -ENOTDIR; + + t = alloca(strlen(path) + 1); + p = path + strspn(path, "/"); + + while (true) { + e = p + strcspn(p, "/"); + p = e + strspn(e, "/"); + + if (*p == 0) + return 0; + + memcpy(t, path, e - path); + t[e - path] = 0; + + if (prefix && shl__path_startswith(prefix, t)) + continue; + + r = mkdir(t, mode); + if (r < 0 && errno != EEXIST) + return -errno; + } +} + +static int shl__mkdir_p(const char *prefix, const char *path, mode_t mode) +{ + int r; + + r = shl__mkdir_parents(prefix, path, mode); + if (r < 0) + return r; + + r = mkdir(path, mode); + if (r < 0 && (errno != EEXIST || shl__is_dir(path) <= 0)) + return -errno; + + return 0; +} + +int shl_mkdir_p(const char *path, mode_t mode) +{ + return shl__mkdir_p(NULL, path, mode); +} + +int shl_mkdir_p_prefix(const char *prefix, const char *path, mode_t mode) +{ + return shl__mkdir_p(prefix, path, mode); +} + +/* + * Time + */ + +uint64_t shl_now(clockid_t clock) +{ + struct timespec ts; + + clock_gettime(clock, &ts); + + return (uint64_t)ts.tv_sec * 1000000LL + + (uint64_t)ts.tv_nsec / 1000LL; +} + +/* + * Ratelimit + * Modelled after Linux' lib/ratelimit.c by Dave Young + * , which is licensed GPLv2. + */ + +bool shl_ratelimit_test(struct shl_ratelimit *r) +{ + uint64_t ts; + + if (!r || r->interval <= 0 || r->burst <= 0) + return true; + + ts = shl_now(CLOCK_MONOTONIC); + + if (r->begin <= 0 || r->begin + r->interval < ts) { + r->begin = ts; + r->num = 0; + goto good; + } else if (r->num < r->burst) { + goto good; + } + + return false; + +good: + ++r->num; + return true; +} diff --git a/src/shared/shl_util.h b/src/shared/shl_util.h index 6ce25fa..16f5e6b 100644 --- a/src/shared/shl_util.h +++ b/src/shared/shl_util.h @@ -17,6 +17,7 @@ #include #include #include +#include #include "shl_macro.h" /* strict atoi */ @@ -80,11 +81,20 @@ static inline int shl_atoi_z(const char *str, void *shl_greedy_realloc(void **mem, size_t *size, size_t need); void *shl_greedy_realloc0(void **mem, size_t *size, size_t need); +void *shl_greedy_realloc_t(void **arr, size_t *cnt, size_t need, size_t ts); +void *shl_greedy_realloc0_t(void **arr, size_t *cnt, size_t need, size_t ts); + +#define SHL_GREEDY_REALLOC_T(array, count, need) \ + shl_greedy_realloc_t((void**)&(array), &count, need, sizeof(*(array))) +#define SHL_GREEDY_REALLOC0_T(array, count, need) \ + shl_greedy_realloc0_t((void**)&(array), &count, need, sizeof(*(array))) /* string helpers */ char *shl_strcat(const char *first, const char *second); _shl_sentinel_ char *shl_strjoin(const char *first, ...); +int shl_strsplit_n(const char *str, size_t len, const char *sep, char ***out); +int shl_strsplit(const char *str, const char *sep, char ***out); static inline char *shl_startswith(const char *str, const char *prefix) { @@ -103,7 +113,7 @@ static inline void shl_strv_freep(char ***strv) shl_strv_free(*strv); } -#define _shl_cleanup_strv_ _shl_cleanup_(shl_strv_freep) +#define _shl_strv_free_ _shl_cleanup_(shl_strv_freep) /* quoted strings */ @@ -111,5 +121,43 @@ char shl_qstr_unescape_char(char c); void shl_qstr_decode_n(char *str, size_t length); int shl_qstr_tokenize_n(const char *str, size_t length, char ***out); int shl_qstr_tokenize(const char *str, char ***out); +int shl_qstr_join(char **strv, char **out); + +/* mkdir */ + +int shl_mkdir_p(const char *path, mode_t mode); +int shl_mkdir_p_prefix(const char *prefix, const char *path, mode_t mode); + +/* time */ + +uint64_t shl_now(clockid_t clock); + +/* ratelimit */ + +struct shl_ratelimit { + uint64_t interval; + uint64_t begin; + unsigned burst; + unsigned num; +}; + +#define SHL_RATELIMIT_DEFINE(_name, _interval, _burst) \ + struct shl_ratelimit _name = { (_interval), (_burst), 0, 0 } + +#define SHL_RATELIMIT_INIT(_v, _interval, _burst) do { \ + struct shl_ratelimit *_r = &(_v); \ + _r->interval = (_interval); \ + _r->burst = (_burst); \ + _r->num = 0; \ + _r->begin = 0; \ + } while (false) + +#define SHL_RATELIMIT_RESET(_v) do { \ + struct shl_ratelimit *_r = &(_v); \ + _r->num = 0; \ + _r->begin = 0; \ + } while (false) + +bool shl_ratelimit_test(struct shl_ratelimit *r); #endif /* SHL_UTIL_H */