diff options
author | Jörg Frings-Fürst <debian@jff-webhosting.net> | 2022-10-24 21:04:00 +0200 |
---|---|---|
committer | Jörg Frings-Fürst <debian@jff-webhosting.net> | 2022-10-24 21:04:00 +0200 |
commit | 3422d8db505630a70bc89a4eee7db927b8e5ec2f (patch) | |
tree | f0c6e1ba7db9991f2bd38c9169f9921bfe5e61d8 /src/string.c | |
parent | df5167db909a88fb8e16dd20b37442495a6ac059 (diff) | |
parent | aab49e5a013c53ae812a143fe41add74e0677a61 (diff) |
Merge branch 'feature/upstream' into develop
Diffstat (limited to 'src/string.c')
-rw-r--r-- | src/string.c | 309 |
1 files changed, 300 insertions, 9 deletions
diff --git a/src/string.c b/src/string.c index 354a409..c14f1b5 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> @@ -40,11 +42,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; @@ -605,14 +602,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 +760,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); @@ -819,7 +820,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 +841,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; +} |