[beast: 13/57] SFI: add the most commonly needed string handling utilities
- From: Tim Janik <timj src gnome org>
- To: commits-list gnome org
- Cc:
- Subject: [beast: 13/57] SFI: add the most commonly needed string handling utilities
- Date: Sun, 23 Jul 2017 09:58:53 +0000 (UTC)
commit 5a79691d74daefd9cfc44879d937cbcb84086119
Author: Tim Janik <timj gnu org>
Date: Fri Jul 14 03:08:34 2017 +0200
SFI: add the most commonly needed string handling utilities
Signed-off-by: Tim Janik <timj gnu org>
sfi/strings.cc | 1307 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++
sfi/strings.hh | 190 ++++++++
2 files changed, 1497 insertions(+), 0 deletions(-)
---
diff --git a/sfi/strings.cc b/sfi/strings.cc
new file mode 100644
index 0000000..e1426aa
--- /dev/null
+++ b/sfi/strings.cc
@@ -0,0 +1,1307 @@
+// This Source Code Form is licensed MPL-2.0: http://mozilla.org/MPL/2.0
+#include "strings.hh"
+#include "bcore.hh"
+#include <cmath>
+#include <stdio.h>
+#include <stdlib.h>
+#include <libintl.h>
+#include <iconv.h>
+#include <errno.h>
+
+#include <glib.h> // g_unichar_*
+
+namespace Bse {
+
+namespace Unicode {
+extern inline unichar tolower (unichar uc) { return g_unichar_tolower (uc); }
+extern inline unichar toupper (unichar uc) { return g_unichar_toupper (uc); }
+extern inline unichar totitle (unichar uc) { return g_unichar_totitle (uc); }
+} // Unicode
+
+// === String ===
+/// Reproduce a string @a s for @a count times.
+String
+string_multiply (const String &s,
+ uint64 count)
+{
+ if (count == 1)
+ return s;
+ else if (count & 1)
+ {
+ String tmp = string_multiply (s, count - 1);
+ tmp += s;
+ return tmp;
+ }
+ else if (count)
+ {
+ String tmp = string_multiply (s, count / 2);
+ tmp += tmp;
+ return tmp;
+ }
+ else
+ return "";
+}
+
+/** Enforce a canonical charset for a string.
+ * Convert all chars in @a string that are not listed as @a valid_chars with @a substitute.
+ */
+String
+string_canonify (const String &string, const String &valid_chars, const String &substitute)
+{
+ const size_t l = string.size();
+ const char *valids = valid_chars.c_str(), *p = string.c_str();
+ size_t i;
+ for (i = 0; i < l; i++)
+ if (!strchr (valids, p[i]))
+ goto rewrite_string;
+ return string; // only ref increment
+ rewrite_string:
+ String d = string.substr (0, i);
+ d += substitute;
+ for (++i; i < l; i++)
+ if (strchr (valids, p[i]))
+ d += p[i];
+ else
+ d += substitute;
+ return d;
+}
+
+/// Check if string_canonify() would modify @a string.
+bool
+string_is_canonified (const String &string, const String &valid_chars)
+{
+ const size_t l = string.size();
+ const char *valids = valid_chars.c_str(), *p = string.c_str();
+ for (size_t i = 0; i < l; i++)
+ if (!strchr (valids, p[i]))
+ return false;
+ return true;
+}
+
+/// Returns a string containing all of a-z.
+String
+string_set_a2z ()
+{
+ return "abcdefghijklmnopqrstuvwxyz";
+}
+
+/// Returns a string containing all of A-Z.
+String
+string_set_A2Z ()
+{
+ return "ABCDEFGHIJKLMNOPQRSTUVWXYZ";
+}
+
+/// Returns a string containing all of 0-9, A-Z and a-z.
+String
+string_set_ascii_alnum ()
+{
+ return "0123456789" + string_set_A2Z() + string_set_a2z();
+}
+
+/// Convert all string characters into Unicode lower case characters.
+String
+string_tolower (const String &str)
+{
+ String s (str);
+ for (size_t i = 0; i < s.size(); i++)
+ s[i] = Unicode::tolower (s[i]);
+ return s;
+}
+
+/// Convert all string characters into Unicode upper case characters.
+String
+string_toupper (const String &str)
+{
+ String s (str);
+ for (size_t i = 0; i < s.size(); i++)
+ s[i] = Unicode::toupper (s[i]);
+ return s;
+}
+
+/// Convert all string characters into Unicode title characters.
+String
+string_totitle (const String &str)
+{
+ String s (str);
+ for (size_t i = 0; i < s.size(); i++)
+ s[i] = Unicode::totitle (s[i]);
+ return s;
+}
+
+/// Capitalize words, so the first letter is upper case, the rest lower case.
+String
+string_capitalize (const String &str, size_t maxn)
+{
+ String s (str);
+ bool wasalpha = false;
+ for (size_t i = 0; i < s.size(); i++)
+ {
+ const bool atalpha = isalpha (s[i]);
+ if (!wasalpha && atalpha)
+ {
+ if (maxn == 0)
+ break;
+ s[i] = Unicode::toupper (s[i]);
+ maxn--;
+ }
+ else
+ s[i] = Unicode::tolower (s[i]);
+ wasalpha = atalpha;
+ }
+ return s;
+}
+
+#define STACK_BUFFER_SIZE 3072
+
+static inline String
+current_locale_vprintf (const char *format, va_list vargs)
+{
+ va_list pargs;
+ char buffer[STACK_BUFFER_SIZE];
+ buffer[0] = 0;
+ va_copy (pargs, vargs);
+ const int l = vsnprintf (buffer, sizeof (buffer), format, pargs);
+ va_end (pargs);
+ std::string string;
+ if (l < 0)
+ string = format; // error?
+ else if (size_t (l) < sizeof (buffer))
+ string = String (buffer, l);
+ else
+ {
+ string.resize (l + 1);
+ va_copy (pargs, vargs);
+ const int j = vsnprintf (&string[0], string.size(), format, pargs);
+ va_end (pargs);
+ string.resize (std::min (l, std::max (j, 0)));
+ }
+ return string;
+}
+
+static inline String
+posix_locale_vprintf (const char *format, va_list vargs)
+{
+ Lib::ScopedPosixLocale posix_locale_scope; // pushes POSIX/C locale for this scope
+ return current_locale_vprintf (format, vargs);
+}
+
+/// Formatted printing ala vprintf() into a String, using the POSIX/C locale.
+String
+string_vprintf (const char *format, va_list vargs)
+{
+ return posix_locale_vprintf (format, vargs);
+}
+
+/// Formatted printing like string_vprintf using the current locale.
+String
+string_locale_vprintf (const char *format, va_list vargs)
+{
+ return current_locale_vprintf (format, vargs);
+}
+
+static StringVector
+string_whitesplit (const String &string, size_t maxn)
+{
+ static const char whitespaces[] = " \t\n\r\f\v";
+ StringVector sv;
+ size_t i, l = 0;
+ for (i = 0; i < string.size() && sv.size() < maxn; i++)
+ if (strchr (whitespaces, string[i]))
+ {
+ if (i > l)
+ sv.push_back (string.substr (l, i - l));
+ l = i + 1;
+ }
+ i = string.size();
+ if (i > l)
+ sv.push_back (string.substr (l, i - l));
+ return sv;
+}
+
+/// Split a string, using @a splitter as delimiter.
+/// Passing "" as @a splitter will split the string at whitespace positions.
+StringVector
+string_split (const String &string, const String &splitter, size_t maxn)
+{
+ if (splitter == "")
+ return string_whitesplit (string, maxn);
+ StringVector sv;
+ size_t i, l = 0, k = splitter.size();
+ for (i = 0; i < string.size() && sv.size() < maxn; i++)
+ if (string.substr (i, k) == splitter)
+ {
+ if (i >= l)
+ sv.push_back (string.substr (l, i - l));
+ l = i + k;
+ }
+ i = string.size();
+ if (i >= l)
+ sv.push_back (string.substr (l, i - l));
+ return sv;
+}
+
+/// Split a string, using any of the @a splitchars as delimiter.
+/// Passing "" as @a splitter will split the string between all position.
+StringVector
+string_split_any (const String &string, const String &splitchars, size_t maxn)
+{
+ StringVector sv;
+ size_t i, l = 0;
+ if (splitchars.empty())
+ {
+ for (i = 0; i < string.size() && sv.size() < maxn; i++)
+ sv.push_back (string.substr (i, 1));
+ if (i < string.size())
+ sv.push_back (string.substr (i, string.size() - i));
+ }
+ else
+ {
+ const char *schars = splitchars.c_str();
+ l = 0;
+ for (i = 0; i < string.size() && sv.size() < maxn; i++)
+ if (strchr (schars, string[i]))
+ {
+ if (i >= l)
+ sv.push_back (string.substr (l, i - l));
+ l = i + 1;
+ }
+ i = string.size();
+ if (i >= l)
+ sv.push_back (string.substr (l, i - l));
+ }
+ return sv;
+}
+
+/// Remove empty elements from a string vector.
+void
+string_vector_erase_empty (StringVector &svector)
+{
+ for (size_t i = svector.size(); i; i--)
+ {
+ const size_t idx = i - 1;
+ if (svector[idx].empty())
+ svector.erase (svector.begin() + idx);
+ }
+}
+
+/// Left-strip all elements of a string vector, see string_lstrip().
+void
+string_vector_lstrip (StringVector &svector)
+{
+ for (auto &s : svector)
+ s = string_lstrip (s);
+}
+
+/// Right-strip all elements of a string vector, see string_rstrip().
+void
+string_vector_rstrip (StringVector &svector)
+{
+ for (auto &s : svector)
+ s = string_rstrip (s);
+}
+
+/// Strip all elements of a string vector, see string_strip().
+void
+string_vector_strip (StringVector &svector)
+{
+ for (auto &s : svector)
+ s = string_strip (s);
+}
+
+/** Join a number of strings.
+ * Join a string vector into a single string, using @a junctor inbetween each pair of strings.
+ */
+String
+string_join (const String &junctor, const StringVector &strvec)
+{
+ String s;
+ if (strvec.size())
+ s = strvec[0];
+ for (uint i = 1; i < strvec.size(); i++)
+ s += junctor + strvec[i];
+ return s;
+}
+
+/** Interpret a string as boolean value.
+ * Interpret the string as number, "ON"/"OFF" or distinguish "false"/"true" or "yes"/"no" by starting letter.
+ * For empty strings, @a fallback is returned.
+ */
+bool
+string_to_bool (const String &string, bool fallback)
+{
+ return cstring_to_bool (string.c_str(), fallback);
+}
+
+bool
+cstring_to_bool (const char *string, bool fallback)
+{
+ if (!string)
+ return fallback;
+ const char *p = string;
+ // skip spaces
+ while (*p && isspace (*p))
+ p++;
+ // ignore signs
+ if (p[0] == '-' || p[0] == '+')
+ {
+ p++;
+ // skip spaces
+ while (*p && isspace (*p))
+ p++;
+ }
+ // handle numbers
+ if (p[0] >= '0' && p[0] <= '9')
+ return 0 != string_to_uint (p);
+ // handle special words
+ if (strncasecmp (p, "ON", 2) == 0)
+ return 1;
+ if (strncasecmp (p, "OFF", 3) == 0)
+ return 0;
+ // empty string
+ if (!p[0])
+ return fallback;
+ // anything else needs to resemble "yes" or "true"
+ return strchr ("YyTt", p[0]);
+}
+
+/// Convert a boolean value into a string.
+String
+string_from_bool (bool value)
+{
+ return String (value ? "1" : "0");
+}
+
+/// Parse a string into a 64bit unsigned integer, optionally specifying the expected number base.
+uint64
+string_to_uint (const String &string, size_t *consumed, uint base)
+{
+ const char *const start = string.c_str(), *p = start;
+ while (*p == ' ' || *p == '\n' || *p == '\t' || *p == '\r')
+ p++;
+ const bool hex = p[0] == '0' && (p[1] == 'X' || p[1] == 'x');
+ const char *const number = hex ? p + 2 : p;
+ char *endptr = NULL;
+ const uint64 result = strtoull (number, &endptr, hex ? 16 : base);
+ if (consumed)
+ {
+ if (!endptr || endptr <= number)
+ *consumed = 0;
+ else
+ *consumed = endptr - start;
+ }
+ return result;
+}
+
+/// Convert a 64bit unsigned integer into a string.
+String
+string_from_uint (uint64 value)
+{
+ return string_format ("%u", value);
+}
+
+/// Checks if a string contains a digit, optionally preceeded by whitespaces.
+bool
+string_has_int (const String &string)
+{
+ const char *p = string.c_str();
+ while (*p == ' ' || *p == '\n' || *p == '\t' || *p == '\r')
+ p++;
+ return p[0] >= '0' && p[0] <= '9';
+}
+
+/// Parse a string into a 64bit integer, optionally specifying the expected number base.
+int64
+string_to_int (const String &string, size_t *consumed, uint base)
+{
+ const char *const start = string.c_str(), *p = start;
+ while (*p == ' ' || *p == '\n' || *p == '\t' || *p == '\r')
+ p++;
+ const bool hex = p[0] == '0' && (p[1] == 'X' || p[1] == 'x');
+ const char *const number = hex ? p + 2 : p;
+ char *endptr = NULL;
+ const int64 result = strtoll (number, &endptr, hex ? 16 : base);
+ if (consumed)
+ {
+ if (!endptr || endptr <= number)
+ *consumed = 0;
+ else
+ *consumed = endptr - start;
+ }
+ return result;
+}
+
+/// Convert a 64bit signed integer into a string.
+String
+string_from_int (int64 value)
+{
+ return string_format ("%d", value);
+}
+
+static long double
+libc_strtold (const char *nptr, char **endptr)
+{
+ const long double result = strtold (nptr, endptr);
+ if (std::isnan (result) && std::signbit (result) == 0)
+ {
+ const char *p = nptr;
+ while (isspace (*p))
+ p++;
+ if (strncasecmp (p, "-nan", 4) == 0)
+ return -result; // glibc-2.19 doesn't get the NAN sign right
+ }
+ return result;
+}
+
+/// Parse a double from a string ala strtod(), trying locale specific characters and POSIX/C formatting.
+long double
+posix_locale_strtold (const char *nptr, char **endptr)
+{
+ Lib::ScopedPosixLocale posix_locale_scope; // pushes POSIX/C locale for this scope
+ char *fail_pos = NULL;
+ const long double val = libc_strtold (nptr, &fail_pos);
+ if (endptr)
+ *endptr = fail_pos;
+ return val;
+}
+
+/// Parse a double from a string ala strtod(), trying locale specific characters and POSIX/C formatting.
+long double
+current_locale_strtold (const char *nptr, char **endptr)
+{
+ char *fail_pos_1 = NULL;
+ const long double val_1 = posix_locale_strtold (nptr, &fail_pos_1);
+ if (fail_pos_1 && fail_pos_1[0] != 0)
+ {
+ char *fail_pos_2 = NULL;
+ const long double val_2 = libc_strtold (nptr, &fail_pos_2);
+ if (fail_pos_2 > fail_pos_1)
+ {
+ if (endptr)
+ *endptr = fail_pos_2;
+ return val_2;
+ }
+ }
+ if (endptr)
+ *endptr = fail_pos_1;
+ return val_1;
+}
+
+/// Parse a double from a string, trying locale specific characters and POSIX/C formatting.
+double
+string_to_double (const String &string)
+{
+ return current_locale_strtold (string.c_str(), NULL);
+}
+
+/// Similar to string_to_double(const String&), but returns the first failing character position in @a
endptr.
+double
+string_to_double (const char *dblstring, const char **endptr)
+{
+ return current_locale_strtold (dblstring, (char**) endptr);
+}
+
+/// Convert a float into a string, using the POSIX/C locale.
+String
+string_from_float (float value)
+{
+ if (std::isnan (value))
+ return std::signbit (value) ? "-NaN" : "+NaN";
+ if (std::isinf (value))
+ return std::signbit (value) ? "-Infinity" : "+Infinity";
+ return string_format ("%.7g", value);
+}
+
+/// Convert a double into a string, using the POSIX/C locale.
+String
+string_from_double (double value)
+{
+ if (std::isnan (value))
+ return std::signbit (value) ? "-NaN" : "+NaN";
+ if (std::isinf (value))
+ return std::signbit (value) ? "-Infinity" : "+Infinity";
+ return string_format ("%.17g", value);
+}
+
+/// Parse a string into a list of doubles, expects ';' as delimiter.
+vector<double>
+string_to_double_vector (const String &string)
+{
+ vector<double> dvec;
+ const char *spaces = " \t\n";
+ const char *obrace = "{([";
+ const char *delims = ";";
+ const char *cbrace = "])}";
+ const char *number = "+-0123456789eE.,";
+ const char *s = string.c_str();
+ /* skip spaces */
+ while (*s && strchr (spaces, *s))
+ s++;
+ /* skip opening brace */
+ if (*s && strchr (obrace, *s))
+ s++;
+ const char *d = s;
+ while (*d && !strchr (cbrace, *d))
+ {
+ while (*d && strchr (spaces, *d)) /* skip spaces */
+ d++;
+ s = d; /* start of number */
+ if (!*d || (!strchr (number, *d) && /* ... if any */
+ !strchr (delims, *d)))
+ break;
+ while (*d && strchr (number, *d)) /* pass across number */
+ d++;
+ dvec.push_back (string_to_double (String (s, d - s)));
+ while (*d && strchr (spaces, *d)) /* skip spaces */
+ d++;
+ if (*d && strchr (delims, *d))
+ d++; /* eat delimiter */
+ }
+ // printerr ("vector: %d: %s\n", dvec.size(), string_from_double_vector (dvec).c_str());
+ return dvec;
+}
+
+/// Construct a string out of all double values passed in @a dvec, separated by @a delim.
+String
+string_from_double_vector (const vector<double> &dvec, const String &delim)
+{
+ String s;
+ for (uint i = 0; i < dvec.size(); i++)
+ {
+ if (i > 0)
+ s += delim;
+ s += string_from_double (dvec[i]);
+ }
+ return s;
+}
+
+/// Returns a String describing the passed in errno value, similar to strerror().
+String
+string_from_errno (int errno_val)
+{
+ if (errno_val < 0)
+ errno_val = -errno_val; // fixup library return values
+ char buffer[1024] = { 0, };
+ const char *errstr = strerror_r (errno_val, buffer, sizeof (buffer));
+ if (!errstr || !errstr[0]) // fallback for possible strerror_r breakage encountered on _GNU_SOURCE systems
+ return strerror (errno_val);
+ return errstr;
+}
+
+/// Returns whether @a uuid_string contains a properly formatted UUID string.
+bool
+string_is_uuid (const String &uuid_string) /* check uuid formatting */
+{
+ int i, l = uuid_string.size();
+ if (l != 36)
+ return false;
+ // 00000000-0000-0000-0000-000000000000
+ for (i = 0; i < l; i++)
+ if (i == 8 || i == 13 || i == 18 || i == 23)
+ {
+ if (uuid_string[i] != '-')
+ return false;
+ continue;
+ }
+ else if ((uuid_string[i] >= '0' && uuid_string[i] <= '9') ||
+ (uuid_string[i] >= 'a' && uuid_string[i] <= 'f') ||
+ (uuid_string[i] >= 'A' && uuid_string[i] <= 'F'))
+ continue;
+ else
+ return false;
+ return true;
+}
+
+/// Returns whether @a uuid_string1 compares smaller (-1), equal (0) or greater (+1) to @a uuid_string2.
+int
+string_cmp_uuid (const String &uuid_string1, const String &uuid_string2)
+{
+ return strcasecmp (uuid_string1.c_str(), uuid_string2.c_str()); /* good enough for numeric equality and
defines stable order */
+}
+
+/// Returns whether @a string starts with @a fragment.
+bool
+string_startswith (const String &string, const String &fragment)
+{
+ return fragment.size() <= string.size() && 0 == string.compare (0, fragment.size(), fragment);
+}
+
+/// Returns whether @a string ends with @a fragment.
+bool
+string_endswith (const String &string, const String &fragment)
+{
+ return fragment.size() <= string.size() && 0 == string.compare (string.size() - fragment.size(),
fragment.size(), fragment);
+}
+
+static inline bool
+c_isalnum (uint8 c)
+{
+ return (c >= 'A' && c <= 'Z') || (c >= 'a' && c <= 'z') || (c >= '0' && c <= '9');
+}
+
+static inline char
+identifier_char_canon (char c)
+{
+ if (c >= '0' && c <= '9')
+ return c;
+ else if (c >= 'A' && c <= 'Z')
+ return c - 'A' + 'a';
+ else if (c >= 'a' && c <= 'z')
+ return c;
+ else
+ return '-';
+}
+
+static inline bool
+identifier_match (const char *str1, const char *str2)
+{
+ while (*str1 && *str2)
+ {
+ const uint8 s1 = identifier_char_canon (*str1++);
+ const uint8 s2 = identifier_char_canon (*str2++);
+ if (s1 != s2)
+ return false;
+ }
+ return *str1 == 0 && *str2 == 0;
+}
+
+static bool
+match_identifier_detailed (const String &ident, const String &tail)
+{
+ assert_return (ident.size() >= tail.size(), false);
+ const char *word = ident.c_str() + ident.size() - tail.size();
+ if (word > ident.c_str()) // allow partial matches on word boundary only
+ {
+ if (c_isalnum (word[-1]) && c_isalnum (word[0])) // no word boundary
+ return false;
+ }
+ return identifier_match (word, tail.c_str());
+}
+
+/// Variant of string_match_identifier() that matches @a tail against @a ident at word boundary.
+bool
+string_match_identifier_tail (const String &ident, const String &tail)
+{
+ return ident.size() >= tail.size() && match_identifier_detailed (ident, tail);
+}
+
+/// Check equality of strings canonicalized to "[0-9a-z_]+".
+bool
+string_match_identifier (const String &ident1, const String &ident2)
+{
+ return ident1.size() == ident2.size() && match_identifier_detailed (ident1, ident2);
+}
+
+/// Extract the full function name from __PRETTY_FUNCTION__.
+/// See also BSE_SIMPLE_FUNCTION.
+String
+string_from_pretty_function_name (const char *cxx_pretty_function)
+{
+ // get rid of g++'s anon prefixes
+ const String p1 = string_replace (cxx_pretty_function, "{anonymous}::", "");
+ // get rid of clang++'s anon prefixes
+ const String p2 = string_replace (p1, "(anonymous namespace)::", "");
+ const char *const pretty_function = p2.c_str();
+ /* finding the function name is non-trivial in the presence of function pointer
+ * return types. the following code assumes the function name preceedes the
+ * first opening parenthesis not followed by a star.
+ */
+ const char *op = strchr (pretty_function, '(');
+ while (op && op[1] == '*')
+ op = strchr (op + 1, '(');
+ if (!op)
+ return pretty_function;
+ // *op == '(' && op[1] != '*'
+ const char *last = op - 1;
+ while (last >= pretty_function && strchr (" \t\n", *last))
+ last--; // skip spaces before '('
+ if (last < pretty_function)
+ return pretty_function;
+ // scan across function name characters
+ const char *first = last;
+ while (first >= pretty_function && strchr
("0123456789_ABCDEFGHIJKLMNOPQRSTUVWXYZ:abcdefghijklmnopqrstuvwxyz$", *first))
+ first--;
+ String result = String (first + 1, last - first);
+ return result;
+}
+
+/// Escape text like a C string.
+/// Returns a string that escapes all characters with a backslash '\\' that need escaping in C language
string syntax.
+String
+string_to_cescape (const String &str)
+{
+ String buffer;
+ for (String::const_iterator it = str.begin(); it != str.end(); it++)
+ {
+ const uint8 d = *it;
+ if (d == '\a') buffer += "\\a";
+ else if (d == '\b') buffer += "\\b";
+ else if (d == '\t') buffer += "\\t";
+ else if (d == '\n') buffer += "\\n";
+ else if (d == '\v') buffer += "\\v";
+ else if (d == '\f') buffer += "\\f";
+ else if (d == '\r') buffer += "\\r";
+ else if (d == '"') buffer += "\\\"";
+ else if (d == '\\') buffer += "\\\\";
+ else if (d < 32 || d > 126)
+ buffer += string_format ("\\%03o", d);
+ else
+ buffer += d;
+ }
+ return buffer;
+}
+
+/// Returns a string as C string including double quotes.
+String
+string_to_cquote (const String &str)
+{
+ return String() + "\"" + string_to_cescape (str) + "\"";
+}
+
+/// Parse a possibly quoted C string into regular string.
+String
+string_from_cquote (const String &input)
+{
+ uint i = 0;
+ if (i < input.size() && (input[i] == '"' || input[i] == '\''))
+ {
+ const char qchar = input[i];
+ i++;
+ String out;
+ bool be = false;
+ while (i < input.size() && (input[i] != qchar || be))
+ {
+ if (!be && input[i] == '\\')
+ be = true;
+ else
+ {
+ if (be)
+ switch (input[i])
+ {
+ uint k, oc;
+ case '0': case '1': case '2': case '3':
+ case '4': case '5': case '6': case '7':
+ k = MIN (input.size(), i + 3);
+ oc = input[i++] - '0';
+ while (i < k && input[i] >= '0' && input[i] <= '7')
+ oc = oc * 8 + input[i++] - '0';
+ out += char (oc);
+ i--;
+ break;
+ case 'a': out += '\a'; break;
+ case 'n': out += '\n'; break;
+ case 'r': out += '\r'; break;
+ case 't': out += '\t'; break;
+ case 'b': out += '\b'; break;
+ case 'f': out += '\f'; break;
+ case 'v': out += '\v'; break;
+ default: out += input[i]; break;
+ }
+ else
+ out += input[i];
+ be = false;
+ }
+ i++;
+ }
+ if (i < input.size() && input[i] == qchar)
+ {
+ i++;
+ if (i < input.size())
+ return input; // extraneous characters after string quotes
+ return out;
+ }
+ else
+ return input; // unclosed string quotes
+ }
+ else if (i == input.size())
+ return input; // empty string arg: ""
+ else
+ return input; // missing string quotes
+}
+
+static const char *whitespaces = " \t\v\f\n\r";
+
+/// Strip whitespaces from the left of a string.
+String
+string_lstrip (const String &input)
+{
+ uint64 i = 0;
+ while (i < input.size() && strchr (whitespaces, input[i]))
+ i++;
+ return i ? input.substr (i) : input;
+}
+
+/// Strip whitespaces from the right of a string.
+String
+string_rstrip (const String &input)
+{
+ uint64 i = input.size();
+ while (i > 0 && strchr (whitespaces, input[i - 1]))
+ i--;
+ return i < input.size() ? input.substr (0, i) : input;
+}
+
+/// Strip whitespaces from the left and right of a string.
+String
+string_strip (const String &input)
+{
+ uint64 a = 0;
+ while (a < input.size() && strchr (whitespaces, input[a]))
+ a++;
+ uint64 b = input.size();
+ while (b > 0 && strchr (whitespaces, input[b - 1]))
+ b--;
+ if (a == 0 && b == input.size())
+ return input;
+ else if (b == 0)
+ return "";
+ else
+ return input.substr (a, b - a);
+}
+
+/// Replace substring @a marker in @a input with @a replacement, at most @a maxn times.
+String
+string_replace (const String &input, const String &marker, const String &replacement, size_t maxn)
+{
+ String s = input;
+ size_t i = s.find (marker);
+ while (i != String::npos && maxn-- > 0)
+ {
+ s = s.substr (0, i) + replacement + s.substr (i + marker.size());
+ i = s.find (marker, i + replacement.size());
+ }
+ return s;
+}
+
+
+/// Replace all occouranes of @a match in @a input with @a subst.
+String
+string_substitute_char (const String &input, const char match, const char subst)
+{
+ String output = input;
+ if (match != subst)
+ for (String::size_type i = 0; i < output.size(); i++)
+ if (output.data()[i] == match)
+ output[i] = subst; // unshares string
+ return output;
+}
+
+/** Produce hexdump of a memory region.
+ * Each output line consists of its hexadecimal offset, 16 hexadecimal bytes and the ASCII representation of
the same 16 bytes.
+ */
+String
+string_hexdump (const void *addr, size_t length, size_t initial_offset)
+{
+ // 000000d0 00 34 00 00 08 00 00 00 40 00 00 00 61 00 00 00 |.4......@...a...|
+ const unsigned char *data = (const unsigned char*) addr;
+ size_t i;
+ String out, cx, cc = "|";
+ for (i = 0; i < length;)
+ {
+ if (i % 8 == 0)
+ cx += " ";
+ cx += string_format (" %02x", data[i]);
+ cc += string_format ("%c", data[i] < ' ' || data[i] > '~' ? '.' : data[i]);
+ i++;
+ if (i && i % 16 == 0)
+ {
+ cc += "|";
+ out += string_format ("%08x%s %s\n", initial_offset + i - 16, cx.c_str(), cc.c_str());
+ cx = "";
+ cc = "|";
+ }
+ }
+ if (i % 16)
+ {
+ for (; i % 16; i++)
+ {
+ if (i % 8 == 0)
+ cx += " ";
+ cx += " ";
+ }
+ cc += "|";
+ out += string_format ("%08x%s %s\n", initial_offset + i - 16, cx.c_str(), cc.c_str());
+ }
+ return out;
+}
+
+/// Fill a memory area with a 32-bit quantitiy.
+void
+memset4 (uint32 *mem, uint32 filler, uint length)
+{
+ static_assert (sizeof (*mem) == 4, "");
+ static_assert (sizeof (filler) == 4, "");
+ static_assert (sizeof (wchar_t) == 4, "");
+ wmemset ((wchar_t*) mem, filler, length);
+}
+
+/**
+ * Search for @a prefix in @a svector and return the matching element.
+ * If multiple matches are possible, the last one is returned.
+ * @returns @a fallback if no match was found.
+ */
+String
+string_vector_find (const StringVector &svector, const String &prefix, const String &fallback)
+{
+ for (size_t i = svector.size(); i > 0; i--)
+ {
+ const String &s = svector[i-1];
+ if (s.size() >= prefix.size() && strncmp (s.data(), prefix.data(), prefix.size()) == 0)
+ return s;
+ }
+ return fallback;
+}
+
+/**
+ * Search for @a prefix in @a svector and return reminder of the matching string.
+ * If multiple matches are possible, the last one is returned.
+ * @returns @a fallback if no match was found.
+ */
+String
+string_vector_find_value (const StringVector &svector, const String &prefix, const String &fallback)
+{
+ for (size_t i = svector.size(); i > 0; i--)
+ {
+ const String &s = svector[i-1];
+ if (s.size() >= prefix.size() && strncmp (s.data(), prefix.data(), prefix.size()) == 0)
+ return s.substr (prefix.size());
+ }
+ return fallback;
+}
+
+/// Construct a StringVector from a NULL terminated list of string arguments.
+StringVector
+cstrings_to_vector (const char *s, ...)
+{
+ StringVector sv;
+ if (s)
+ {
+ sv.push_back (s);
+ va_list args;
+ va_start (args, s);
+ s = va_arg (args, const char*);
+ while (s)
+ {
+ sv.push_back (s);
+ s = va_arg (args, const char*);
+ }
+ va_end (args);
+ }
+ return sv;
+}
+
+// === String Options ===
+#define is_sep(c) (c == ';' || c == ':')
+#define is_spacesep(c) (isspace (c) || is_sep (c))
+#define find_sep(str) (strpbrk (str, ";:"))
+
+static void
+string_option_add (const String &assignment,
+ vector<String> *option_namesp,
+ vector<String> &option_values,
+ const String &empty_default,
+ const String *filter)
+{
+ assert_return ((option_namesp != NULL) ^ (filter != NULL));
+ const char *n = assignment.c_str();
+ while (isspace (*n))
+ n++;
+ const char *p = n;
+ while (isalnum (*p) || *p == '-' || *p == '_')
+ p++;
+ const String name = String (n, p - n);
+ if (filter && name != *filter)
+ return;
+ while (isspace (*p))
+ p++;
+ const String value = *p == '=' ? String (p + 1) : empty_default;
+ if (!name.empty() && (*p == '=' || *p == 0)) // valid name
+ {
+ if (!filter)
+ option_namesp->push_back (name);
+ option_values.push_back (value);
+ }
+}
+
+static void
+string_options_split_filtered (const String &option_string,
+ vector<String> *option_namesp,
+ vector<String> &option_values,
+ const String &empty_default,
+ const String *filter)
+{
+ const char *s = option_string.c_str();
+ while (s)
+ {
+ // find next separator
+ const char *b = find_sep (s);
+ string_option_add (String (s, b ? b - s : strlen (s)), option_namesp, option_values, empty_default,
filter);
+ s = b ? b + 1 : NULL;
+ }
+}
+
+/// Split an option list string into name/value pairs.
+void
+string_options_split (const String &option_string,
+ vector<String> &option_names,
+ vector<String> &option_values,
+ const String &empty_default)
+{
+ string_options_split_filtered (option_string, &option_names, option_values, empty_default, NULL);
+}
+
+static String
+string_option_find_value (const String &option_string,
+ const String &option)
+{
+ vector<String> option_names, option_values;
+ string_options_split_filtered (option_string, NULL, option_values, "1", &option);
+ return option_values.empty() ? "0" : option_values[option_values.size() - 1];
+}
+
+/// Retrieve the option value from an options list separated by ':' or ';'.
+String
+string_option_get (const String &option_string,
+ const String &option)
+{
+ return string_option_find_value (option_string, option);
+}
+
+/// Check if an option is set/unset in an options list string.
+bool
+string_option_check (const String &option_string,
+ const String &option)
+{
+ const String value = string_option_find_value (option_string, option);
+ return string_to_bool (value, true);
+}
+
+// == Strings ==
+Strings::Strings (CS &s1)
+{ push_back (s1); }
+Strings::Strings (CS &s1, CS &s2)
+{ push_back (s1); push_back (s2); }
+Strings::Strings (CS &s1, CS &s2, CS &s3)
+{ push_back (s1); push_back (s2); push_back (s3); }
+Strings::Strings (CS &s1, CS &s2, CS &s3, CS &s4)
+{ push_back (s1); push_back (s2); push_back (s3); push_back (s4); }
+Strings::Strings (CS &s1, CS &s2, CS &s3, CS &s4, CS &s5)
+{ push_back (s1); push_back (s2); push_back (s3); push_back (s4); push_back (s5); }
+Strings::Strings (CS &s1, CS &s2, CS &s3, CS &s4, CS &s5, CS &s6)
+{ push_back (s1); push_back (s2); push_back (s3); push_back (s4); push_back (s5); push_back (s6); }
+Strings::Strings (CS &s1, CS &s2, CS &s3, CS &s4, CS &s5, CS &s6, CS &s7)
+{ push_back (s1); push_back (s2); push_back (s3); push_back (s4); push_back (s5); push_back (s6); push_back
(s7); }
+Strings::Strings (CS &s1, CS &s2, CS &s3, CS &s4, CS &s5, CS &s6, CS &s7, CS &s8)
+{ push_back (s1); push_back (s2); push_back (s3); push_back (s4); push_back (s5); push_back (s6);
+ push_back (s7); push_back (s8); }
+Strings::Strings (CS &s1, CS &s2, CS &s3, CS &s4, CS &s5, CS &s6, CS &s7, CS &s8, CS &s9)
+{ push_back (s1); push_back (s2); push_back (s3); push_back (s4); push_back (s5); push_back (s6);
+ push_back (s7); push_back (s8); push_back (s9); }
+Strings::Strings (CS &s1, CS &s2, CS &s3, CS &s4, CS &s5, CS &s6, CS &s7, CS &s8, CS &s9, CS &sA)
+{ push_back (s1); push_back (s2); push_back (s3); push_back (s4); push_back (s5); push_back (s6);
+ push_back (s7); push_back (s8); push_back (s9); push_back (sA); }
+Strings::Strings (CS &s1, CS &s2, CS &s3, CS &s4, CS &s5, CS &s6, CS &s7, CS &s8, CS &s9, CS &sA, CS &sB)
+{ push_back (s1); push_back (s2); push_back (s3); push_back (s4); push_back (s5); push_back (s6);
+ push_back (s7); push_back (s8); push_back (s9); push_back (sA); push_back (sB); }
+Strings::Strings (CS &s1, CS &s2, CS &s3, CS &s4, CS &s5, CS &s6, CS &s7, CS &s8, CS &s9, CS &sA, CS &sB, CS
&sC)
+{ push_back (s1); push_back (s2); push_back (s3); push_back (s4); push_back (s5); push_back (s6);
+ push_back (s7); push_back (s8); push_back (s9); push_back (sA); push_back (sB); push_back (sC); }
+
+// === Charset Conversions ===
+static bool
+unalias_encoding (String &name)
+{
+ /* list of common aliases for MIME encodings */
+ static const char *encoding_aliases[] = {
+ /* alias MIME (GNU CANONICAL) */
+ "UTF8", "UTF-8",
+ /* ascii */
+ "646", "ASCII",
+ "ISO_646.IRV:1983", "ASCII",
+ "CP20127", "ASCII",
+ /* iso8859 aliases */
+ "LATIN1", "ISO-8859-1",
+ "LATIN2", "ISO-8859-2",
+ "LATIN3", "ISO-8859-3",
+ "LATIN4", "ISO-8859-4",
+ "LATIN5", "ISO-8859-9",
+ "LATIN6", "ISO-8859-10",
+ "LATIN7", "ISO-8859-13",
+ "LATIN8", "ISO-8859-14",
+ "LATIN9", "ISO-8859-15",
+ "LATIN10", "ISO-8859-16",
+ "ISO8859-1", "ISO-8859-1",
+ "ISO8859-2", "ISO-8859-2",
+ "ISO8859-3", "ISO-8859-3",
+ "ISO8859-4", "ISO-8859-4",
+ "ISO8859-5", "ISO-8859-5",
+ "ISO8859-6", "ISO-8859-6",
+ "ISO8859-7", "ISO-8859-7",
+ "ISO8859-8", "ISO-8859-8",
+ "ISO8859-9", "ISO-8859-9",
+ "ISO8859-13", "ISO-8859-13",
+ "ISO8859-15", "ISO-8859-15",
+ "CP28591", "ISO-8859-1",
+ "CP28592", "ISO-8859-2",
+ "CP28593", "ISO-8859-3",
+ "CP28594", "ISO-8859-4",
+ "CP28595", "ISO-8859-5",
+ "CP28596", "ISO-8859-6",
+ "CP28597", "ISO-8859-7",
+ "CP28598", "ISO-8859-8",
+ "CP28599", "ISO-8859-9",
+ "CP28603", "ISO-8859-13",
+ "CP28605", "ISO-8859-15",
+ /* EUC aliases */
+ "eucCN", "GB2312",
+ "IBM-eucCN", "GB2312",
+ "dechanzi", "GB2312",
+ "eucJP", "EUC-JP",
+ "IBM-eucJP", "EUC-JP",
+ "sdeckanji", "EUC-JP",
+ "eucKR", "EUC-KR",
+ "IBM-eucKR", "EUC-KR",
+ "deckorean", "EUC-KR",
+ "eucTW", "EUC-TW",
+ "IBM-eucTW", "EUC-TW",
+ "CNS11643", "EUC-TW",
+ "CP20866", "KOI8-R",
+ /* misc */
+ "PCK", "SHIFT_JIS",
+ "SJIS", "SHIFT_JIS",
+ };
+ /* find a MIME encoding from alias list */
+ for (uint i = 0; i < sizeof (encoding_aliases) / sizeof (encoding_aliases[0]); i += 2)
+ if (strcasecmp (encoding_aliases[i], name.c_str()) == 0)
+ {
+ name = encoding_aliases[i + 1];
+ return true;
+ }
+ /* last resort, try upper-casing the encoding name */
+ String upper = name;
+ for (uint i = 0; i < upper.size(); i++)
+ if (upper[i] >= 'a' && upper[i] <= 'z')
+ upper[i] += 'A' - 'a';
+ if (upper != name)
+ {
+ name = upper;
+ return true;
+ }
+ /* alias not found */
+ return false;
+}
+
+static iconv_t
+aliased_iconv_open (const String &tocode,
+ const String &fromcode)
+{
+ const iconv_t icNONE = (iconv_t) -1;
+ iconv_t cd = iconv_open (tocode.c_str(), fromcode.c_str());
+ if (cd != icNONE)
+ return cd;
+ /* lookup destination encoding from alias and retry */
+ String to_encoding = tocode;
+ if (unalias_encoding (to_encoding))
+ {
+ cd = iconv_open (to_encoding.c_str(), fromcode.c_str());
+ if (cd != icNONE)
+ return cd;
+ /* lookup source and destination encoding from alias and retry */
+ String from_encoding = fromcode;
+ if (unalias_encoding (from_encoding))
+ {
+ cd = iconv_open (to_encoding.c_str(), from_encoding.c_str());
+ if (cd != icNONE)
+ return cd;
+ }
+ }
+ /* lookup source encoding from alias and retry */
+ String from_encoding = fromcode;
+ if (unalias_encoding (from_encoding))
+ {
+ cd = iconv_open (tocode.c_str(), from_encoding.c_str());
+ if (cd != icNONE)
+ return cd;
+ }
+ return icNONE; /* encoding not found */
+}
+
+/** Convert a string from one encoding to another.
+ * Convert @a input_string from encoding @a from_charset to @a to_charset, returning @a output_string.
+ * Interpret unknown characters according to @a fallback_charset. Use @a output_mark in place of
unconvertible characters.
+ * Returns whether the conversion was successful.
+ */
+bool
+text_convert (const String &to_charset,
+ String &output_string,
+ const String &from_charset,
+ const String &input_string,
+ const String &fallback_charset,
+ const String &output_mark)
+{
+ output_string = "";
+ const iconv_t icNONE = (iconv_t) -1;
+ iconv_t alt_cd = icNONE, cd = aliased_iconv_open (to_charset, from_charset);
+ if (cd == icNONE)
+ return false; /* failed to perform the requested conversion */
+ const char *iptr = input_string.c_str();
+ size_t ilength = input_string.size();
+ char obuffer[1024]; /* declared outside loop to spare re-initialization */
+ String alt_charset = fallback_charset;
+ while (ilength)
+ {
+ /* convert */
+ char *optr = obuffer;
+ size_t olength = sizeof (obuffer);
+ size_t n = iconv (cd, const_cast<char**> (&iptr), &ilength, &optr, &olength);
+ /* transfer output */
+ output_string.append (obuffer, optr - obuffer);
+ /* handle conversion errors */
+ if (ilength && /* ignore past end errors */
+ n == (size_t) -1)
+ {
+ if (errno == EINVAL || /* unfinished multibyte sequences follows (near end of string) */
+ errno == EILSEQ) /* invalid multibyte sequence follows */
+ {
+ /* open alternate converter */
+ if (alt_cd == icNONE && alt_charset.size())
+ {
+ alt_cd = aliased_iconv_open (to_charset, alt_charset);
+ alt_charset = ""; /* don't retry iconv_open() */
+ }
+ size_t former_ilength = ilength;
+ if (alt_cd != icNONE)
+ {
+ /* convert from alt_charset */
+ optr = obuffer;
+ olength = sizeof (obuffer);
+ n = iconv (alt_cd, const_cast<char**> (&iptr), &ilength, &optr, &olength);
+ /* transfer output */
+ output_string.append (obuffer, optr - obuffer);
+ }
+ if (ilength == former_ilength)
+ {
+ /* failed alternate conversion, mark invalid character */
+ output_string += output_mark;
+ iptr++;
+ ilength--;
+ }
+ }
+ else /* all other errors are considered fatal */
+ return false; /* failed to perform the requested conversion */
+ }
+ }
+ iconv_close (cd);
+ if (alt_cd != icNONE)
+ iconv_close (alt_cd);
+ return true;
+
+}
+
+const char*
+strerror()
+{
+ return strerror (errno);
+}
+
+} // Bse
diff --git a/sfi/strings.hh b/sfi/strings.hh
new file mode 100644
index 0000000..9209610
--- /dev/null
+++ b/sfi/strings.hh
@@ -0,0 +1,190 @@
+// This Source Code Form is licensed MPL-2.0: http://mozilla.org/MPL/2.0
+#ifndef __BSE_STRINGS_HH__
+#define __BSE_STRINGS_HH__
+
+#include <sfi/cxxaux.hh>
+#include <sfi/formatter.hh>
+#include <cstring> // strerror
+
+namespace Bse {
+typedef std::string String;
+
+// == Macros ==
+#ifdef BSE_CONVENIENCE
+/// Produce a const char* string, wrapping @a str into C-style double quotes.
+#define CQUOTE(str) BSE_CQUOTE(str)
+/// Create a Bse::StringVector, from a const char* C-style array.
+#define STRING_VECTOR_FROM_ARRAY(ConstCharArray) BSE_STRING_VECTOR_FROM_ARRAY(ConstCharArray)
+#endif // BSE_CONVENIENCE
+
+// == C-String ==
+bool cstring_to_bool (const char *string, bool fallback = false);
+
+// == String Formatting ==
+template<class... Args> String string_format (const char *format, const Args &...args) BSE_PRINTF
(1, 0);
+template<class... Args> String string_locale_format (const char *format, const Args &...args) BSE_PRINTF
(1, 0);
+String string_vprintf (const char *format, va_list vargs);
+String string_locale_vprintf (const char *format, va_list vargs);
+
+// == String ==
+String string_multiply (const String &s, uint64 count);
+String string_canonify (const String &s, const String &valid_chars, const
String &substitute);
+bool string_is_canonified (const String &s, const String &valid_chars);
+String string_set_a2z ();
+String string_set_A2Z ();
+String string_set_ascii_alnum ();
+String string_tolower (const String &str);
+String string_toupper (const String &str);
+String string_totitle (const String &str);
+String string_capitalize (const String &str, size_t maxn = size_t (-1));
+StringVector string_split (const String &string, const String &splitter = "",
size_t maxn = size_t (-1));
+StringVector string_split_any (const String &string, const String &splitchars =
"", size_t maxn = size_t (-1));
+String string_join (const String &junctor, const StringVector &strvec);
+bool string_to_bool (const String &string, bool fallback = false);
+String string_from_bool (bool value);
+uint64 string_to_uint (const String &string, size_t *consumed = NULL, uint
base = 10);
+String string_from_uint (uint64 value);
+bool string_has_int (const String &string);
+int64 string_to_int (const String &string, size_t *consumed = NULL, uint
base = 10);
+String string_from_int (int64 value);
+String string_from_float (float value);
+double string_to_double (const String &string);
+double string_to_double (const char *dblstring, const char **endptr);
+String string_from_double (double value);
+inline String string_from_float (double value) { return string_from_double
(value); }
+inline double string_to_float (const String &string) { return string_to_double
(string); }
+vector<double> string_to_double_vector (const String &string);
+String string_from_double_vector(const vector<double> &dvec,
+ const String &delim = " ");
+String string_from_errno (int errno_val);
+bool string_is_uuid (const String &uuid_string); /* check uuid
formatting */
+int string_cmp_uuid (const String &uuid_string1,
+ const String &uuid_string2); /* -1=smaller,
0=equal, +1=greater (assuming valid uuid strings) */
+bool string_startswith (const String &string, const String &fragment);
+bool string_endswith (const String &string, const String &fragment);
+bool string_match_identifier (const String &ident1, const String &ident2);
+bool string_match_identifier_tail (const String &ident, const String &tail);
+String string_from_pretty_function_name (const char *cxx_pretty_function);
+String string_to_cescape (const String &str);
+String string_to_cquote (const String &str);
+String string_from_cquote (const String &input);
+String string_hexdump (const void *addr, size_t length, size_t
initial_offset = 0);
+String string_lstrip (const String &input);
+String string_rstrip (const String &input);
+String string_strip (const String &input);
+String string_replace (const String &input, const String &marker, const
String &replacement, size_t maxn = ~size_t (0));
+String string_substitute_char (const String &input, const char match, const char
subst);
+void string_vector_lstrip (StringVector &svector);
+void string_vector_rstrip (StringVector &svector);
+void string_vector_strip (StringVector &svector);
+void string_vector_erase_empty (StringVector &svector);
+String string_vector_find (const StringVector &svector, const String &prefix, const String
&fallback = "");
+String string_vector_find_value (const StringVector &svector, const String &prefix, const String
&fallback = "");
+StringVector cstrings_to_vector (const char*, ...) BSE_SENTINEL;
+void memset4 (uint32 *mem, uint32 filler, uint length);
+long double posix_locale_strtold (const char *nptr, char **endptr);
+long double current_locale_strtold (const char *nptr, char **endptr);
+
+
+// == Templated String Conversions ==
+
+/// Convert a @a string to template argument type, such as bool, int, double.
+template<typename Type> Type string_to_type (const String &string)
+{ static_assert (!sizeof (Type), "string_to_type<>: unsupported Type"); __builtin_unreachable(); }
+
+/// Create a @a string from a templated argument value, such as bool, int, double.
+template<typename Type> String string_from_type (Type value)
+{ static_assert (!sizeof (Type), "string_from_type<>: unsupported Type"); __builtin_unreachable(); }
+
+// specialisations for templated string conversions
+template<> inline double string_to_type<double> (const String &string) { return string_to_double
(string); }
+template<> inline String string_from_type<double> (double value) { return string_from_double
(value); }
+template<> inline float string_to_type<float> (const String &string) { return string_to_float
(string); }
+template<> inline String string_from_type<float> (float value) { return string_from_float
(value); }
+template<> inline bool string_to_type<bool> (const String &string) { return string_to_bool
(string); }
+template<> inline String string_from_type<bool> (bool value) { return string_from_bool
(value); }
+template<> inline int16 string_to_type<int16> (const String &string) { return string_to_int
(string); }
+template<> inline String string_from_type<int16> (int16 value) { return string_from_int
(value); }
+template<> inline uint16 string_to_type<uint16> (const String &string) { return string_to_uint
(string); }
+template<> inline String string_from_type<uint16> (uint16 value) { return string_from_uint
(value); }
+template<> inline int string_to_type<int> (const String &string) { return string_to_int
(string); }
+template<> inline String string_from_type<int> (int value) { return string_from_int
(value); }
+template<> inline uint string_to_type<uint> (const String &string) { return string_to_uint
(string); }
+template<> inline String string_from_type<uint> (uint value) { return string_from_uint
(value); }
+template<> inline int64 string_to_type<int64> (const String &string) { return string_to_int
(string); }
+template<> inline String string_from_type<int64> (int64 value) { return string_from_int
(value); }
+template<> inline uint64 string_to_type<uint64> (const String &string) { return string_to_uint
(string); }
+template<> inline String string_from_type<uint64> (uint64 value) { return string_from_uint
(value); }
+template<> inline String string_to_type<String> (const String &string) { return string; }
+template<> inline String string_from_type<String> (String value) { return value; }
+
+
+// == String Options ==
+bool string_option_check (const String &option_string,
+ const String &option);
+String string_option_get (const String &option_string,
+ const String &option);
+void string_options_split (const String &option_string,
+ vector<String> &option_names,
+ vector<String> &option_values,
+ const String &empty_default = "");
+
+// == Strings ==
+/// Convenience Constructor for StringSeq or std::vector<std::string>
+class Strings : public std::vector<std::string>
+{
+ typedef const std::string CS;
+public:
+ explicit Strings (CS &s1);
+ explicit Strings (CS &s1, CS &s2);
+ explicit Strings (CS &s1, CS &s2, CS &s3);
+ explicit Strings (CS &s1, CS &s2, CS &s3, CS &s4);
+ explicit Strings (CS &s1, CS &s2, CS &s3, CS &s4, CS &s5);
+ explicit Strings (CS &s1, CS &s2, CS &s3, CS &s4, CS &s5, CS &s6);
+ explicit Strings (CS &s1, CS &s2, CS &s3, CS &s4, CS &s5, CS &s6, CS &s7);
+ explicit Strings (CS &s1, CS &s2, CS &s3, CS &s4, CS &s5, CS &s6, CS &s7, CS &s8);
+ explicit Strings (CS &s1, CS &s2, CS &s3, CS &s4, CS &s5, CS &s6, CS &s7, CS &s8, CS &s9);
+ explicit Strings (CS &s1, CS &s2, CS &s3, CS &s4, CS &s5, CS &s6, CS &s7, CS &s8, CS &s9, CS &sA);
+ explicit Strings (CS &s1, CS &s2, CS &s3, CS &s4, CS &s5, CS &s6, CS &s7, CS &s8, CS &s9, CS &sA, CS &sB);
+ explicit Strings (CS &s1, CS &s2, CS &s3, CS &s4, CS &s5, CS &s6, CS &s7, CS &s8, CS &s9, CS &sA, CS &sB,
CS &sC);
+};
+
+// == Charset Conversions ==
+bool text_convert (const String &to_charset,
+ String &output_string,
+ const String &from_charset,
+ const String &input_string,
+ const String &fallback_charset = "ISO-8859-15",
+ const String &output_mark = "");
+
+// == C strings ==
+using ::strerror; // introduce (const char* strerror (int))
+const char* strerror (); // simple wrapper for strerror (errno)
+
+// == Implementations ==
+#define BSE_STRING_VECTOR_FROM_ARRAY(ConstCharArray) ({ \
+ Bse::StringVector __a; \
+ const Bse::uint64 __l = BSE_ARRAY_SIZE (ConstCharArray); \
+ for (Bse::uint64 __ai = 0; __ai < __l; __ai++) \
+ __a.push_back (ConstCharArray[__ai]); \
+ __a; })
+#define BSE_CQUOTE(str) (Bse::string_to_cquote (str).c_str())
+
+/// Formatted printing ala printf() into a String, using the POSIX/C locale.
+template<class... Args> BSE_NOINLINE String
+string_format (const char *format, const Args &...args)
+{
+ return Lib::StringFormatter::format (NULL, format, args...);
+}
+
+/// Formatted printing ala printf() into a String, using the current locale.
+template<class... Args> BSE_NOINLINE String
+string_locale_format (const char *format, const Args &...args)
+{
+ return Lib::StringFormatter::format<Lib::StringFormatter::CURRENT_LOCALE> (NULL, format, args...);
+}
+
+} // Bse
+
+#endif // __BSE_STRINGS_HH__
+
[
Date Prev][
Date Next] [
Thread Prev][
Thread Next]
[
Thread Index]
[
Date Index]
[
Author Index]