summaryrefslogtreecommitdiff
path: root/src/string.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/string.c')
-rw-r--r--src/string.c309
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;
+}