summaryrefslogtreecommitdiff
path: root/src/string.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/string.c')
-rw-r--r--src/string.c382
1 files changed, 354 insertions, 28 deletions
diff --git a/src/string.c b/src/string.c
index 354a409..ae19271 100644
--- a/src/string.c
+++ b/src/string.c
@@ -8,6 +8,8 @@
* either version 2.1 or (at your option) any later version.
*/
#include <errno.h>
+#include <limits.h>
+#include <math.h>
#include <stdbool.h>
#include <stddef.h>
#include <stdint.h>
@@ -25,6 +27,7 @@
* all others pass through
*/
enum HX_quote_selector {
+ HXQUOTE_ALWAYS,
HXQUOTE_ACCEPT,
HXQUOTE_REJECT,
};
@@ -40,11 +43,6 @@ struct HX_quote_rule {
static const char HX_hexenc[16] = "0123456789ABCDEF";
-static __inline__ unsigned int min_uint(unsigned int a, unsigned int b)
-{
- return (a < b) ? a : b;
-}
-
EXPORT_SYMBOL char *HX_basename(const char *s)
{
const char *p;
@@ -214,6 +212,8 @@ EXPORT_SYMBOL char **HX_split(const char *str, const char *delim,
*cp = max;
ret = malloc(sizeof(char *) * (*cp + 1));
+ if (ret == nullptr)
+ return nullptr;
ret[*cp] = NULL;
{
@@ -381,20 +381,48 @@ EXPORT_SYMBOL char *HX_stpltrim(const char *p)
return const_cast1(char *, p);
}
+/**
+ * Helper for substr() function for dealing with negative off/len values
+ * @z: total length of string
+ * @offset: n>=0 specifies offset from the start,
+ * n<0 specifies offset from the end
+ * @len: "length"; n>=0 specifies length to copy (from @offset),
+ * n<0 specifies the byte relative to the end at which to stop
+ *
+ * @abstart: (result) absolute start
+ * @retval: (result) absolute length to copy (from *@abstart)
+ */
+size_t HX_substr_helper(size_t z, long offset, long len, size_t *start)
+{
+ if (offset >= 0)
+ *start = offset;
+ else if (offset == LONG_MIN)
+ *start = z + -static_cast(size_t, LONG_MIN);
+ else
+ *start = z >= static_cast(unsigned long, -offset) ? z + offset : z;
+
+ size_t end;
+ if (len >= 0)
+ end = *start < SIZE_MAX - len ? *start + len : SIZE_MAX;
+ else if (len == LONG_MIN)
+ end = z + -static_cast(unsigned long, LONG_MIN);
+ else
+ end = z >= static_cast(unsigned long, -len) ? z + len : 0;
+ if (end > z)
+ end = z;
+ return end > *start ? end - *start : 0;
+}
+
/* supports negative offsets like scripting languages */
EXPORT_SYMBOL char *HX_strmid(const char *expr, long offset, long length)
{
- char *buffer;
-
- if (offset < 0)
- offset = strlen(expr) + offset;
- if (length < 0)
- length = strlen(expr) - offset + length;
- if ((buffer = malloc(length + 1)) == NULL)
+ size_t start = 0, tocopy = HX_substr_helper(strlen(expr), offset, length, &start);
+ char *buffer = malloc(tocopy + 1);
+ if (buffer == nullptr)
return NULL;
-
- expr += offset;
- return HX_strlcpy(buffer, expr, length + 1);
+ memcpy(buffer, &expr[start], tocopy);
+ buffer[tocopy] = '\0';
+ return buffer;
}
EXPORT_SYMBOL char *HX_strndup(const char *src, size_t size)
@@ -605,14 +633,16 @@ HX_quote_sqlbackslash(char *dest, const char *src, const char *trm)
* Encode @src into BASE-64 according to RFC 4648 and write result to @dest,
* which must be of appropriate size, plus one for a trailing NUL.
*/
-static char *HX_quote_base64(char *d, const char *s)
+static char *HX_quote_base64(char *d, const char *s, char x1, char x2)
{
- static const char a[] =
+ char a[] =
"ABCDEFGHIJKLMNOPQRSTUVWXYZ"
- "abcdefghijklmnopqrstuvwxyz0123456789+/";
+ "abcdefghijklmnopqrstuvwxyz0123456789??";
size_t len = strlen(s);
char *ret = d;
+ a[62] = x1;
+ a[63] = x2;
while (len > 0) {
if (len >= 3) {
len -= 3;
@@ -761,6 +791,8 @@ static size_t HX_quoted_size(const char *s, unsigned int type)
case HXQUOTE_LDAPRDN:
return HX_qsize_bsr(s, HX_quote_rules[type].chars, 2);
case HXQUOTE_BASE64:
+ case HXQUOTE_BASE64URL:
+ case HXQUOTE_BASE64IMAP:
return (strlen(s) + 2) / 3 * 4;
case HXQUOTE_URIENC:
return HX_qsize_bsa(s, HX_quote_rules[type].chars, 2);
@@ -772,7 +804,7 @@ static size_t HX_quoted_size(const char *s, unsigned int type)
EXPORT_SYMBOL char *HX_strquote(const char *src, unsigned int type,
char **free_me)
{
- const struct HX_quote_rule *rule;
+ const struct HX_quote_rule *rule = nullptr;
bool do_quote;
char *tmp;
@@ -781,15 +813,19 @@ EXPORT_SYMBOL char *HX_strquote(const char *src, unsigned int type,
return NULL;
}
/* If quote_chars is NULL, it is clear all chars are to be encoded. */
- rule = &HX_quote_rules[type];
- if (type >= ARRAY_SIZE(HX_quote_rules) || rule->chars == NULL)
+ if (type >= ARRAY_SIZE(HX_quote_rules)) {
do_quote = true;
- else if (rule->selector == HXQUOTE_REJECT)
- do_quote = strpbrk(src, rule->chars) != NULL;
- else if (rule->selector == HXQUOTE_ACCEPT)
- do_quote = HX_strchr2(src, rule->chars) != NULL;
- else
- do_quote = false;
+ } else {
+ rule = &HX_quote_rules[type];
+ if (rule->selector == HXQUOTE_ALWAYS)
+ do_quote = true;
+ else if (rule->selector == HXQUOTE_REJECT)
+ do_quote = strpbrk(src, rule->chars) != NULL;
+ else if (rule->selector == HXQUOTE_ACCEPT)
+ do_quote = HX_strchr2(src, rule->chars) != NULL;
+ else
+ do_quote = false;
+ }
/*
* free_me == NULL implies that we always allocate, even if
* there is nothing to quote.
@@ -819,7 +855,11 @@ EXPORT_SYMBOL char *HX_strquote(const char *src, unsigned int type,
case HXQUOTE_LDAPRDN:
return HX_quote_ldap(*free_me, src, rule->chars);
case HXQUOTE_BASE64:
- return HX_quote_base64(*free_me, src);
+ return HX_quote_base64(*free_me, src, '+', '/');
+ case HXQUOTE_BASE64URL:
+ return HX_quote_base64(*free_me, src, '-', '_');
+ case HXQUOTE_BASE64IMAP:
+ return HX_quote_base64(*free_me, src, '+', ',');
case HXQUOTE_URIENC:
return HX_quote_urlenc(*free_me, src);
case HXQUOTE_SQLSQUOTE:
@@ -836,3 +876,289 @@ EXPORT_SYMBOL char *HX_strupper(char *orig)
*expr = HX_toupper(*expr);
return orig;
}
+
+EXPORT_SYMBOL char *HX_unit_size(char *buf, size_t bufsize,
+ unsigned long long size, unsigned int divisor, unsigned int cutoff)
+{
+ static const char unit_names[] = "\0kMGTPEZYRQ";
+ unsigned int unit_idx = 0;
+ if (divisor == 0)
+ divisor = 1000;
+ if (cutoff == 0) {
+ cutoff = 10000;
+ if (cutoff < divisor)
+ cutoff = divisor;
+ }
+ while (unit_idx < ARRAY_SIZE(unit_names) - 1 && size >= cutoff) {
+ ++unit_idx;
+ size /= divisor;
+ }
+ snprintf(buf, bufsize, "%llu%.1s", size, &unit_names[unit_idx]);
+ return buf;
+}
+
+static inline unsigned long long p_90(unsigned long long x)
+{
+ /* Perform x*9/10, but without the risk of overflow. */
+ return x - x / 10 - !!(x % 10);
+}
+
+EXPORT_SYMBOL char *HX_unit_size_cu(char *buf, size_t bufsize,
+ unsigned long long orig_size, unsigned int divisor)
+{
+ /* No floating point. Take that, coreutils! */
+ static const char unit_names[] = "\0kMGTPEZYRQ";
+ unsigned int unit_idx = 0, last_rem = 0;
+ unsigned long long size = orig_size, gpow = 1, grand_rem;
+ if (divisor == 0)
+ divisor = 1000;
+
+ while (unit_idx < ARRAY_SIZE(unit_names) - 1 && size >= divisor) {
+ ++unit_idx;
+ last_rem = size % divisor;
+ size /= divisor;
+ gpow *= divisor;
+ }
+ if (unit_idx == 0) {
+ snprintf(buf, bufsize, "%llu%.1s", size, &unit_names[unit_idx]);
+ return buf;
+ }
+ grand_rem = orig_size - size * gpow;
+ if (grand_rem != 0) {
+ if (grand_rem > p_90(gpow)) {
+ ++size;
+ last_rem = 0;
+ } else {
+ last_rem *= 10;
+ last_rem /= divisor;
+ ++last_rem;
+ if (last_rem == 10 || (size >= 10 && last_rem > 0)) {
+ ++size;
+ last_rem = 0;
+ }
+ }
+ if (unit_idx < ARRAY_SIZE(unit_names) - 1 && size == divisor) {
+ /* ++size from above may brought size to @divisor again */
+ ++unit_idx;
+ size /= divisor;
+ }
+ }
+ if (size >= 10 && last_rem == 0)
+ snprintf(buf, bufsize, "%llu%.1s", size, &unit_names[unit_idx]);
+ else
+ snprintf(buf, bufsize, "%llu.%01u%.1s", size, last_rem, &unit_names[unit_idx]);
+ return buf;
+}
+
+static unsigned int suffix_power(char u)
+{
+ switch (HX_toupper(u)) {
+ case 'K': return 1;
+ case 'M': return 2;
+ case 'G': return 3;
+ case 'T': return 4;
+ case 'P': return 5;
+ case 'E': return 6;
+ case 'Z': return 7;
+ case 'Y': return 8;
+ case 'R': return 9;
+ case 'Q': return 10;
+ default: return 0;
+ }
+}
+
+EXPORT_SYMBOL double HX_strtod_unit(const char *s, char **out_end, unsigned int exponent)
+{
+ char *end;
+ double q;
+
+ while (HX_isspace(*s))
+ ++s;
+ q = strtod(s, &end);
+ if (exponent == 0)
+ exponent = 1000;
+ if (end == s) {
+ if (out_end != nullptr)
+ *out_end = end;
+ return q;
+ }
+ while (HX_isspace(*end))
+ ++end;
+ unsigned int pwr = suffix_power(*end);
+ if (pwr == 0) {
+ if (out_end != nullptr)
+ *out_end = const_cast(char *, end);
+ return q;
+ }
+ if (out_end != nullptr)
+ *out_end = const_cast(char *, end + 1);
+ return q * pow(exponent, pwr);
+}
+
+EXPORT_SYMBOL unsigned long long HX_strtoull_unit(const char *s,
+ char **out_end, unsigned int exponent)
+{
+ char *end;
+ unsigned long long ipart;
+ unsigned int pwr = 0;
+
+ while (HX_isspace(*s))
+ ++s;
+ ipart = strtoull(s, &end, 10);
+ if (*end == '.') {
+ double q = HX_strtod_unit(s, out_end, exponent);
+ bool lo_ok = q >= nextafter(-static_cast(double, ULLONG_MAX), 0);
+ bool hi_ok = q <= nextafter(static_cast(double, ULLONG_MAX), 0);
+ if (!hi_ok || !lo_ok)
+ return ULLONG_MAX;
+ return q;
+ }
+ if (exponent == 0)
+ exponent = 1000;
+ while (HX_isspace(*end))
+ ++end;
+ pwr = suffix_power(*end);
+ if (pwr == 0) {
+ if (out_end != nullptr)
+ *out_end = end;
+ return ipart;
+ }
+ if (out_end != nullptr)
+ *out_end = const_cast(char *, end + 1);
+ while (pwr-- > 0) {
+ if (ipart >= ULLONG_MAX / exponent) {
+ errno = ERANGE;
+ return ULLONG_MAX;
+ }
+ ipart *= exponent;
+ }
+ return ipart;
+}
+
+#define SECONDS_PER_YEAR 31557600
+#define SECONDS_PER_MONTH 2629800
+
+static const struct {
+ const char name[8];
+ unsigned int len;
+ uint32_t mult;
+} time_multiplier[] = {
+ {"seconds", 7, 1},
+ {"second", 6, 1},
+ {"sec", 3, 1},
+ {"s", 1, 1},
+ {"minutes", 7, 60},
+ {"minute", 6, 60},
+ {"min", 3, 60},
+ {"hours", 5, 3600},
+ {"hour", 4, 3600},
+ {"h", 1, 3600},
+ {"days", 4, 86400},
+ {"day", 3, 86400},
+ {"d", 1, 86400},
+ {"weeks", 5, 604800},
+ {"week", 4, 604800},
+ {"months", 6, SECONDS_PER_MONTH},
+ {"month", 5, SECONDS_PER_MONTH},
+ {"years", 5, SECONDS_PER_YEAR},
+ {"year", 4, SECONDS_PER_YEAR},
+ {"y", 1, SECONDS_PER_YEAR},
+};
+
+EXPORT_SYMBOL unsigned long long HX_strtoull_sec(const char *s, char **out_end)
+{
+ unsigned long long seconds = 0;
+
+ while (*s != '\0') {
+ while (HX_isspace(*s))
+ ++s;
+ if (*s == '-') {
+ break;
+ }
+ char *end = nullptr;
+ unsigned long long num = strtoull(s, &end, 10);
+ if (end == s)
+ break;
+ s = end;
+ while (HX_isspace(*s))
+ ++s;
+ if (!HX_isalpha(*s)) {
+ seconds += num;
+ continue;
+ }
+ unsigned int i;
+ for (i = 0; i < ARRAY_SIZE(time_multiplier); ++i)
+ if (strncmp(s, time_multiplier[i].name,
+ time_multiplier[i].len) == 0 &&
+ !HX_isalpha(s[time_multiplier[i].len]))
+ break;
+ if (i == ARRAY_SIZE(time_multiplier))
+ break;
+ seconds += num * time_multiplier[i].mult;
+ s += time_multiplier[i].len;
+ }
+ if (out_end != nullptr)
+ *out_end = const_cast(char *, s);
+ return seconds;
+}
+
+EXPORT_SYMBOL char *HX_unit_seconds(char *out, size_t outsize,
+ unsigned long long secs, unsigned int flags)
+{
+ unsigned long years = 0, months = 0, weeks = 0, days, hours, mins;
+ char buf[HXSIZEOF_Z64+4];
+ if (flags & HXUNIT_YEARS) {
+ years = secs / SECONDS_PER_YEAR;
+ secs %= SECONDS_PER_YEAR;
+ }
+ if (flags & HXUNIT_MONTHS) {
+ months = secs / SECONDS_PER_MONTH;
+ secs %= SECONDS_PER_MONTH;
+ }
+ if (flags & HXUNIT_WEEKS) {
+ weeks = secs / 604800;
+ secs %= 604800;
+ }
+ days = secs / 86400;
+ secs %= 86400;
+ hours = secs / 3600;
+ secs %= 3600;
+ mins = secs / 60;
+ secs %= 60;
+ *out = '\0';
+ if (years > 0) {
+ snprintf(buf, ARRAY_SIZE(buf), "%luy", years);
+ HX_strlcat(out, buf, outsize);
+ }
+ if (months == 1) {
+ HX_strlcat(out, "1month", outsize);
+ } else if (months > 0) {
+ snprintf(buf, ARRAY_SIZE(buf), "%lumonths", months);
+ HX_strlcat(out, buf, outsize);
+ }
+ if (weeks == 1) {
+ HX_strlcat(out, "1week", outsize);
+ } else if (weeks > 0) {
+ snprintf(buf, ARRAY_SIZE(buf), "%luweeks", weeks);
+ HX_strlcat(out, buf, outsize);
+ }
+ if (days > 0) {
+ snprintf(buf, ARRAY_SIZE(buf), "%lud", days);
+ HX_strlcat(out, buf, outsize);
+ }
+ if (hours > 0) {
+ snprintf(buf, ARRAY_SIZE(buf), "%luh", hours);
+ HX_strlcat(out, buf, outsize);
+ }
+ if (mins > 0) {
+ snprintf(buf, ARRAY_SIZE(buf), "%lumin", mins);
+ HX_strlcat(out, buf, outsize);
+ }
+ if (secs > 0 ||
+ (years == 0 && months == 0 && weeks == 0 &&
+ days == 0 && hours == 0 && mins == 0)) {
+ snprintf(buf, ARRAY_SIZE(buf), "%llus", secs);
+ HX_strlcat(out, buf, outsize);
+ }
+ return out;
+}