summaryrefslogtreecommitdiff
path: root/src/string.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/string.c')
-rw-r--r--src/string.c370
1 files changed, 245 insertions, 125 deletions
diff --git a/src/string.c b/src/string.c
index 3a85fa8..e9c162d 100644
--- a/src/string.c
+++ b/src/string.c
@@ -41,6 +41,26 @@ struct HX_quote_rule {
const char *chars;
};
+/* years, months, weeks, days, hours, minutes, seconds, msecs, musecs, nsecs; */
+enum period_idx {
+ PERIDX_YEARS = 0,
+ PERIDX_MONTHS,
+ PERIDX_WEEKS,
+ PERIDX_DAYS,
+ PERIDX_HOURS,
+ PERIDX_MIN,
+ PERIDX_SEC,
+ PERIDX_MSEC,
+ PERIDX_MUSEC,
+ PERIDX_NSEC,
+ PERIDX_MAX,
+};
+
+struct HX_unit_desc {
+ const char name[8];
+ uint8_t len, pidx;
+};
+
static const char HX_hexenc[] = "0123456789ABCDEF";
EXPORT_SYMBOL char *HX_basename(const char *s)
@@ -217,13 +237,11 @@ EXPORT_SYMBOL char **HX_split(const char *str, const char *delim,
ret[*cp] = NULL;
{
- char *seg, *wp = HX_strdup(str), *bg = wp;
+ char *wp = HX_strdup(str), *bg = wp;
size_t i = 0;
- while (--max > 0) {
- seg = HX_strsep(&wp, delim);
- ret[i++] = HX_strdup(seg);
- }
+ while (--max > 0)
+ ret[i++] = HX_strdup(strtok_r(nullptr, delim, &wp));
ret[i++] = HX_strdup(wp);
free(bg);
@@ -374,13 +392,6 @@ EXPORT_SYMBOL size_t HX_strltrim(char *expr)
return diff;
}
-EXPORT_SYMBOL char *HX_stpltrim(const char *p)
-{
- while (HX_isspace(*p))
- ++p;
- return const_cast1(char *, p);
-}
-
/**
* Helper for substr() function for dealing with negative off/len values
* @z: total length of string
@@ -442,14 +453,6 @@ EXPORT_SYMBOL char *HX_strndup(const char *src, size_t size)
return ret;
}
-EXPORT_SYMBOL size_t HX_strnlen(const char *src, size_t size)
-{
- const char *ptr = src;
- for (; *ptr != '\0' && size > 0; --size, ++ptr)
- ;
- return ptr - src;
-}
-
EXPORT_SYMBOL size_t HX_strrcspn(const char *s, const char *rej)
{
size_t n = strlen(s);
@@ -483,35 +486,6 @@ EXPORT_SYMBOL size_t HX_strrtrim(char *expr)
return s;
}
-EXPORT_SYMBOL char *HX_strsep(char **sp, const char *d)
-{
- char *begin, *end;
-
- if (*sp == NULL || **sp == '\0')
- return NULL;
- begin = *sp;
-
- if (d[0] == '\0' || d[1] == '\0') {
- if (*begin == *d)
- end = begin;
- else if (*begin == '\0')
- end = NULL;
- else
- end = strchr(begin + 1, *d);
- } else {
- end = strpbrk(begin, d);
- }
-
- if (end == NULL) {
- *sp = NULL;
- } else {
- *end++ = '\0';
- *sp = end;
- }
-
- return begin;
-}
-
EXPORT_SYMBOL char *HX_strsep2(char **wp, const char *str)
{
char *ptr, *ret;
@@ -534,8 +508,6 @@ static const struct HX_quote_rule HX_quote_rules[] = {
[HXQUOTE_LDAPFLT] = {HXQUOTE_REJECT, "\n*()\\"},
[HXQUOTE_LDAPRDN] = {HXQUOTE_REJECT, "\n \"#+,;<=>\\"},
[HXQUOTE_URIENC] = {HXQUOTE_ACCEPT, "-.0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ_abcdefghijklmnopqrstuvwxyz~"},
- [HXQUOTE_SQLSQUOTE] = {HXQUOTE_REJECT, "'"},
- [HXQUOTE_SQLBQUOTE] = {HXQUOTE_REJECT, "`"},
};
/**
@@ -605,30 +577,6 @@ static char *HX_quote_backslash(char *dest, const char *src, const char *qc)
return ret;
}
-static char *
-HX_quote_sqlbackslash(char *dest, const char *src, const char *trm)
-{
- char *ret = dest;
- size_t len;
-
- while (*src != '\0') {
- len = strcspn(src, trm);
- if (len > 0) {
- memcpy(dest, src, len);
- dest += len;
- src += len;
- if (*src == '\0')
- break;
- }
- *dest++ = *trm;
- *dest++ = *trm;
- ++src;
- }
-
- *dest = '\0';
- return ret;
-}
-
/**
* 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.
@@ -782,8 +730,6 @@ static size_t HX_quoted_size(const char *s, unsigned int type)
switch (type) {
case HXQUOTE_SQUOTE:
case HXQUOTE_DQUOTE:
- case HXQUOTE_SQLSQUOTE:
- case HXQUOTE_SQLBQUOTE:
return HX_qsize_bsr(s, HX_quote_rules[type].chars, 1);
case HXQUOTE_HTML:
return HX_qsize_html(s);
@@ -862,9 +808,6 @@ EXPORT_SYMBOL char *HX_strquote(const char *src, unsigned int type,
return HX_quote_base64(*free_me, src, '+', ',');
case HXQUOTE_URIENC:
return HX_quote_urlenc(*free_me, src);
- case HXQUOTE_SQLSQUOTE:
- case HXQUOTE_SQLBQUOTE:
- return HX_quote_sqlbackslash(*free_me, src, rule->chars);
}
return NULL;
}
@@ -1068,47 +1011,169 @@ EXPORT_SYMBOL unsigned long long HX_strtoull_unit(const char *s,
return neg ? -ipart : ipart;
}
+static const struct HX_unit_desc txtperiod_utab[] = {
+ {"seconds", 7, PERIDX_SEC},
+ {"second", 6, PERIDX_SEC},
+ {"sec", 3, PERIDX_SEC},
+ {"s", 1, PERIDX_SEC},
+ {"minutes", 7, PERIDX_MIN},
+ {"minute", 6, PERIDX_MIN},
+ {"min", 3, PERIDX_MIN},
+ {"hours", 5, PERIDX_HOURS},
+ {"hour", 4, PERIDX_HOURS},
+ {"h", 1, PERIDX_HOURS},
+ {"days", 4, PERIDX_DAYS},
+ {"day", 3, PERIDX_DAYS},
+ {"d", 1, PERIDX_DAYS},
+ {"weeks", 5, PERIDX_WEEKS},
+ {"week", 4, PERIDX_WEEKS},
+ {"months", 6, PERIDX_MONTHS},
+ {"month", 5, PERIDX_MONTHS},
+ {"years", 5, PERIDX_YEARS},
+ {"year", 4, PERIDX_YEARS},
+ {"y", 1, PERIDX_YEARS},
+ {"msec", 4, PERIDX_MSEC},
+ {"ms", 2, PERIDX_MSEC},
+ {"µsec", 5, PERIDX_MUSEC},
+ {"µs", 3, PERIDX_MUSEC},
+ {"nsec", 4, PERIDX_NSEC},
+ {"ns", 2, PERIDX_NSEC},
+};
+
+static const struct HX_unit_desc iso8601p_dtab[] = {
+ {"D", 1, PERIDX_DAYS},
+ {"M", 1, PERIDX_MONTHS},
+ {"Y", 1, PERIDX_YEARS},
+};
+
+static const struct HX_unit_desc iso8601p_ttab[] = {
+ {"S", 1, PERIDX_SEC},
+ {"M", 1, PERIDX_MIN},
+ {"H", 1, PERIDX_HOURS},
+};
+
/* Numbers also used by systemd — the focus is on longterm averages */
#define SECONDS_PER_YEAR 31557600 /* 365.25 days */
#define SECONDS_PER_MONTH 2629800 /* 1/12th of that year = 30.4375 days */
#define NSEC_PER_SECOND 1000000000ULL
-static const struct {
- const char name[8];
- unsigned int len;
- uint32_t s_mult;
- uint64_t ns_mult;
-} time_multiplier[] = {
- {"seconds", 7, 1, 1 * NSEC_PER_SECOND},
- {"second", 6, 1, 1 * NSEC_PER_SECOND},
- {"sec", 3, 1, 1 * NSEC_PER_SECOND},
- {"s", 1, 1, 1 * NSEC_PER_SECOND},
- {"minutes", 7, 60, 60 * NSEC_PER_SECOND},
- {"minute", 6, 60, 60 * NSEC_PER_SECOND},
- {"min", 3, 60, 60 * NSEC_PER_SECOND},
- {"hours", 5, 3600, 3600 * NSEC_PER_SECOND},
- {"hour", 4, 3600, 3600 * NSEC_PER_SECOND},
- {"h", 1, 3600, 3600 * NSEC_PER_SECOND},
- {"days", 4, 86400, 86400 * NSEC_PER_SECOND},
- {"day", 3, 86400, 86400 * NSEC_PER_SECOND},
- {"d", 1, 86400, 86400 * NSEC_PER_SECOND},
- {"weeks", 5, 604800, 604800 * NSEC_PER_SECOND},
- {"week", 4, 604800, 604800 * NSEC_PER_SECOND},
- {"months", 6, SECONDS_PER_MONTH, SECONDS_PER_MONTH * NSEC_PER_SECOND},
- {"month", 5, SECONDS_PER_MONTH, SECONDS_PER_MONTH * NSEC_PER_SECOND},
- {"years", 5, SECONDS_PER_YEAR, SECONDS_PER_YEAR * NSEC_PER_SECOND},
- {"year", 4, SECONDS_PER_YEAR, SECONDS_PER_YEAR * NSEC_PER_SECOND},
- {"y", 1, SECONDS_PER_YEAR, SECONDS_PER_YEAR * NSEC_PER_SECOND},
- {"msec", 4, 0, 1000000},
- {"ms", 2, 0, 1000000},
- {"µsec", 5, 0, 1000},
- {"µs", 3, 0, 1000},
- {"nsec", 4, 0, 1},
- {"ns", 2, 0, 1},
+static const uint64_t sec_mult[PERIDX_MAX] = {
+ [PERIDX_YEARS] = SECONDS_PER_YEAR,
+ [PERIDX_MONTHS] = SECONDS_PER_MONTH,
+ [PERIDX_WEEKS] = 604800,
+ [PERIDX_DAYS] = 86400,
+ [PERIDX_HOURS] = 3600,
+ [PERIDX_MIN] = 60,
+ [PERIDX_SEC] = 1,
+ [PERIDX_MSEC] = 0,
+ [PERIDX_MUSEC] = 0,
+ [PERIDX_NSEC] = 0,
+};
+
+static const uint64_t nsec_mult[PERIDX_MAX] = {
+ [PERIDX_YEARS] = NSEC_PER_SECOND * SECONDS_PER_YEAR,
+ [PERIDX_MONTHS] = NSEC_PER_SECOND * SECONDS_PER_MONTH,
+ [PERIDX_WEEKS] = NSEC_PER_SECOND * 604800,
+ [PERIDX_DAYS] = NSEC_PER_SECOND * 86400,
+ [PERIDX_HOURS] = NSEC_PER_SECOND * 3600,
+ [PERIDX_MIN] = NSEC_PER_SECOND * 60,
+ [PERIDX_SEC] = NSEC_PER_SECOND,
+ [PERIDX_MSEC] = 1000000,
+ [PERIDX_MUSEC] = 1000,
+ [PERIDX_NSEC] = 1,
};
-static unsigned long long HX_strtoull_time(const char *s, char **out_end, bool nsec)
+/**
+ * Take a textual period string ("1 minute 5 seconds") and break it down.
+ * @s: input string
+ * @utab: allowed unit suffixes
+ * @mtab: multiplication table
+ * @out_end: parsing stopping point
+ *
+ * Returns an errno.
+ */
+static unsigned long long HX_strtoull_period(const char *s,
+ const struct HX_unit_desc *utab, size_t usize, const uint64_t *mtab,
+ size_t msize, char **out_end)
+{
+ unsigned long long quant = 0;
+
+ while (*s != '\0') {
+ while (HX_isspace(*s))
+ ++s;
+ const char *numbegin = s;
+ if (*s == '-')
+ break;
+ char *end = nullptr;
+ unsigned long long num = strtoull(s, &end, 10);
+ if (num == ULLONG_MAX && errno == ERANGE)
+ return num;
+ double frac = 0;
+ bool have_frac = *end == '.';
+ if (have_frac)
+ frac = strtod(s, &end);
+ if (end == s)
+ break;
+ s = end;
+ while (HX_isspace(*s))
+ ++s;
+ unsigned int i;
+ for (i = 0; i < usize; ++i)
+ if (strncasecmp(s, utab[i].name, utab[i].len) == 0 &&
+ /* Ensure a word boundary is present */
+ !HX_isalpha(s[utab[i].len]))
+ break;
+ if (i == usize) {
+ if ((!have_frac && num == 0) || (have_frac && frac == 0))
+ /* 0 is the same no matter what unit, take it */
+ continue;
+ s = numbegin;
+ break;
+ }
+ unsigned long long mult = mtab[utab[i].pidx];
+ if (have_frac) {
+ double v = frac * mult;
+ if (v >= static_cast(double, ULLONG_MAX)) {
+ /*
+ * As ULLONG_MAX gets promoted to double, its
+ * value may _increase_, therefore here we use
+ * >= and not >.
+ */
+ if (out_end != nullptr)
+ *out_end = const_cast(char *, numbegin);
+ errno = ERANGE;
+ return ULLONG_MAX;
+ }
+ num = v;
+ } else {
+ if (mult > 0 && num > ULLONG_MAX / mult) {
+ if (out_end != nullptr)
+ *out_end = const_cast(char *, numbegin);
+ errno = ERANGE;
+ return ULLONG_MAX;
+ }
+ num *= mult;
+ }
+ if (num > ULLONG_MAX - quant) {
+ if (out_end != nullptr)
+ *out_end = const_cast(char *, numbegin);
+ errno = ERANGE;
+ return ULLONG_MAX;
+ }
+ quant += num;
+ s += utab[i].len;
+ }
+ if (out_end != nullptr)
+ *out_end = const_cast(char *, s);
+ errno = 0;
+ return quant;
+}
+
+static unsigned long long HX_strtoull_iso8601p_2(const char *s,
+ const uint64_t *mtab, size_t msize, char **out_end)
{
+ const struct HX_unit_desc *utab = iso8601p_dtab;
+ size_t usize = ARRAY_SIZE(iso8601p_dtab);
unsigned long long quant = 0;
while (*s != '\0') {
@@ -1117,6 +1182,14 @@ static unsigned long long HX_strtoull_time(const char *s, char **out_end, bool n
const char *numbegin = s;
if (*s == '-')
break;
+ if (HX_toupper(*s) == 'T') {
+ if (utab != iso8601p_dtab)
+ break;
+ utab = iso8601p_ttab;
+ usize = ARRAY_SIZE(iso8601p_ttab);
+ ++s;
+ continue;
+ }
char *end = nullptr;
unsigned long long num = strtoull(s, &end, 10);
if (num == ULLONG_MAX && errno == ERANGE)
@@ -1131,45 +1204,92 @@ static unsigned long long HX_strtoull_time(const char *s, char **out_end, bool n
while (HX_isspace(*s))
++s;
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]))
+ /* No word boundary check for 8601 mode */
+ for (i = 0; i < usize; ++i)
+ if (strncmp(s, utab[i].name, utab[i].len) == 0)
break;
- if (i == ARRAY_SIZE(time_multiplier)) {
+ if (i == usize) {
if ((!have_frac && num == 0) || (have_frac && frac == 0))
/* 0 is the same no matter what unit, take it */
continue;
s = numbegin;
break;
}
- unsigned long long mult = nsec ? time_multiplier[i].ns_mult : time_multiplier[i].s_mult;
+ unsigned long long mult = mtab[utab[i].pidx];
if (have_frac) {
- quant += frac * mult;
+ double v = frac * mult;
+ if (v >= static_cast(double, ULLONG_MAX)) {
+ /*
+ * As ULLONG_MAX gets promoted to double, its
+ * value may _increase_, therefore here we use
+ * >= and not >.
+ */
+ if (out_end != nullptr)
+ *out_end = const_cast(char *, numbegin);
+ errno = ERANGE;
+ return ULLONG_MAX;
+ }
+ num = v;
} else {
- if (mult > 0 && num >= ULLONG_MAX / mult) {
+ if (mult > 0 && num > ULLONG_MAX / mult) {
if (out_end != nullptr)
*out_end = const_cast(char *, numbegin);
errno = ERANGE;
return ULLONG_MAX;
}
- quant += num * mult;
+ num *= mult;
}
- s += time_multiplier[i].len;
+ if (num > ULLONG_MAX - quant) {
+ if (out_end != nullptr)
+ *out_end = const_cast(char *, numbegin);
+ errno = ERANGE;
+ return ULLONG_MAX;
+ }
+ quant += num;
+ s += utab[i].len;
}
if (out_end != nullptr)
*out_end = const_cast(char *, s);
+ errno = 0;
return quant;
}
+static bool looks_like_iso8601(const char *s)
+{
+ if (s[0] != 'P')
+ return false;
+ if (HX_isdigit(s[1]))
+ return true;
+ if (s[1] == 'T' && HX_isdigit(s[2]))
+ return true;
+ return false;
+}
+
EXPORT_SYMBOL unsigned long long HX_strtoull_sec(const char *s, char **out_end)
{
- return HX_strtoull_time(s, out_end, false);
+ if (looks_like_iso8601(s))
+ return HX_strtoull_iso8601p_2(&s[1], sec_mult, ARRAY_SIZE(sec_mult), out_end);
+ return HX_strtoull_period(s,
+ txtperiod_utab, ARRAY_SIZE(txtperiod_utab),
+ sec_mult, ARRAY_SIZE(sec_mult), out_end);
}
EXPORT_SYMBOL unsigned long long HX_strtoull_nsec(const char *s, char **out_end)
{
- return HX_strtoull_time(s, out_end, true);
+ if (looks_like_iso8601(s))
+ return HX_strtoull_iso8601p_2(&s[1], nsec_mult, ARRAY_SIZE(nsec_mult), out_end);
+ return HX_strtoull_period(s,
+ txtperiod_utab, ARRAY_SIZE(txtperiod_utab),
+ nsec_mult, ARRAY_SIZE(nsec_mult), out_end);
+}
+
+EXPORT_SYMBOL unsigned long long HX_strtoull8601p_sec(const char *s, char **out_end)
+{
+ if (*s == 'P')
+ return HX_strtoull_iso8601p_2(&s[1], sec_mult, ARRAY_SIZE(sec_mult), out_end);
+ if (out_end != nullptr)
+ *out_end = const_cast(char *, s);
+ return 0;
}
EXPORT_SYMBOL char *HX_unit_seconds(char *out, size_t outsize,