X-Git-Url: https://git.notmuchmail.org/git?p=notmuch;a=blobdiff_plain;f=util%2Fstring-util.c;h=18125309ebe9ed33c048f928e02d1e6b35edca28;hp=44f8cd3a252ae2ba409ad1861202398a76e56c6f;hb=60ac94fe58635f9c40724afa0f35965fc9ff1afc;hpb=9ff72a83bda69e6c064bd8be9f201a4626bff54e diff --git a/util/string-util.c b/util/string-util.c index 44f8cd3a..18125309 100644 --- a/util/string-util.c +++ b/util/string-util.c @@ -13,13 +13,17 @@ * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License - * along with this program. If not, see http://www.gnu.org/licenses/ . + * along with this program. If not, see https://www.gnu.org/licenses/ . * * Author: Jani Nikula */ #include "string-util.h" +#include "talloc.h" + +#include +#include char * strtok_len (char *s, const char *delim, size_t *len) @@ -32,3 +36,222 @@ strtok_len (char *s, const char *delim, size_t *len) return *len ? s : NULL; } + +const char * +strtok_len_c (const char *s, const char *delim, size_t *len) +{ + /* strtok_len is already const-safe, but we can't express both + * versions in the C type system. */ + return strtok_len ((char*)s, delim, len); +} + +char * +sanitize_string (const void *ctx, const char *str) +{ + char *out, *loop; + + if (! str) + return NULL; + + out = talloc_strdup (ctx, str); + if (! out) + return NULL; + + for (loop = out; *loop; loop++) { + if (*loop == '\t' || *loop == '\n') + *loop = ' '; + else if ((unsigned char)(*loop) < 32) + *loop = '?'; + } + + return out; +} + +static int +is_unquoted_terminator (unsigned char c) +{ + return c == 0 || c <= ' ' || c == ')'; +} + +int +make_boolean_term (void *ctx, const char *prefix, const char *term, + char **buf, size_t *len) +{ + const char *in; + char *out; + size_t needed = 3; + int need_quoting = 0; + + /* Do we need quoting? To be paranoid, we quote anything + * containing a quote or '(', even though these only matter at the + * beginning, and anything containing non-ASCII text. */ + if (! term[0]) + need_quoting = 1; + for (in = term; *in && !need_quoting; in++) + if (is_unquoted_terminator (*in) || *in == '"' || *in == '(' + || (unsigned char)*in > 127) + need_quoting = 1; + + if (need_quoting) + for (in = term; *in; in++) + needed += (*in == '"') ? 2 : 1; + else + needed = strlen (term) + 1; + + /* Reserve space for the prefix */ + if (prefix) + needed += strlen (prefix) + 1; + + if ((*buf == NULL) || (needed > *len)) { + *len = 2 * needed; + *buf = talloc_realloc (ctx, *buf, char, *len); + } + + if (! *buf) { + errno = ENOMEM; + return -1; + } + + out = *buf; + + /* Copy in the prefix */ + if (prefix) { + strcpy (out, prefix); + out += strlen (prefix); + *out++ = ':'; + } + + if (! need_quoting) { + strcpy (out, term); + return 0; + } + + /* Quote term by enclosing it in double quotes and doubling any + * internal double quotes. */ + *out++ = '"'; + in = term; + while (*in) { + if (*in == '"') + *out++ = '"'; + *out++ = *in++; + } + *out++ = '"'; + *out = '\0'; + + return 0; +} + +static const char* +skip_space (const char *str) +{ + while (*str && isspace ((unsigned char) *str)) + ++str; + return str; +} + +int +parse_boolean_term (void *ctx, const char *str, + char **prefix_out, char **term_out) +{ + int err = EINVAL; + *prefix_out = *term_out = NULL; + + /* Parse prefix */ + str = skip_space (str); + const char *pos = strchr (str, ':'); + if (! pos || pos == str) + goto FAIL; + *prefix_out = talloc_strndup (ctx, str, pos - str); + if (! *prefix_out) { + err = ENOMEM; + goto FAIL; + } + ++pos; + + /* Implement de-quoting compatible with make_boolean_term. */ + if (*pos == '"') { + char *out = talloc_array (ctx, char, strlen (pos)); + int closed = 0; + if (! out) { + err = ENOMEM; + goto FAIL; + } + *term_out = out; + /* Skip the opening quote, find the closing quote, and + * un-double doubled internal quotes. */ + for (++pos; *pos; ) { + if (*pos == '"') { + ++pos; + if (*pos != '"') { + /* Found the closing quote. */ + closed = 1; + pos = skip_space (pos); + break; + } + } + *out++ = *pos++; + } + /* Did the term terminate without a closing quote or is there + * trailing text after the closing quote? */ + if (!closed || *pos) + goto FAIL; + *out = '\0'; + } else { + const char *start = pos; + /* Check for text after the boolean term. */ + while (! is_unquoted_terminator (*pos)) + ++pos; + if (*skip_space (pos)) { + err = EINVAL; + goto FAIL; + } + /* No trailing text; dup the string so the caller can free + * it. */ + *term_out = talloc_strndup (ctx, start, pos - start); + if (! *term_out) { + err = ENOMEM; + goto FAIL; + } + } + return 0; + + FAIL: + talloc_free (*prefix_out); + talloc_free (*term_out); + errno = err; + return -1; +} + +int +strcmp_null (const char *s1, const char *s2) +{ + if (s1 && s2) + return strcmp (s1, s2); + else if (! s1 && ! s2) + return 0; + else if (s1) + return 1; /* s1 (non-NULL) is greater than s2 (NULL) */ + else + return -1; /* s1 (NULL) is less than s2 (non-NULL) */ +} + +int +strcase_equal (const void *a, const void *b) +{ + return strcasecmp (a, b) == 0; +} + +unsigned int +strcase_hash (const void *ptr) +{ + const char *s = ptr; + + /* This is the djb2 hash. */ + unsigned int hash = 5381; + while (s && *s) { + hash = ((hash << 5) + hash) + tolower (*s); + s++; + } + + return hash; +}