diff options
Diffstat (limited to 'src/string.c')
| -rw-r--r-- | src/string.c | 370 |
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, |
