diff options
Diffstat (limited to 'src/string-utils.c')
-rw-r--r-- | src/string-utils.c | 185 |
1 files changed, 185 insertions, 0 deletions
diff --git a/src/string-utils.c b/src/string-utils.c new file mode 100644 index 0000000..f15b307 --- /dev/null +++ b/src/string-utils.c @@ -0,0 +1,185 @@ +/* + * Copyright (C) 2012-2021 Robin Haberkorn + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * 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/>. + */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include <glib.h> + +#include "sciteco.h" +#include "undo.h" +#include "string-utils.h" + +/** + * Get echoable (printable) version of a given string. + * + * This converts all control characters to printable + * characters without tabs, line feeds, etc. + * That's also why it can safely return a null-terminated string. + * Useful for displaying Q-Register names and TECO code. + * + * @memberof teco_string_t + */ +gchar * +teco_string_echo(const gchar *str, gsize len) +{ + gchar *ret, *p; + + p = ret = g_malloc(len*2 + 1); + + for (guint i = 0; i < len; i++) { + if (TECO_IS_CTL(str[i])) { + *p++ = '^'; + *p++ = TECO_CTL_ECHO(str[i]); + } else { + *p++ = str[i]; + } + } + *p = '\0'; + + return ret; +} + +/** @memberof teco_string_t */ +void +teco_string_get_coord(const gchar *str, guint pos, guint *line, guint *column) +{ + *line = *column = 1; + + for (guint i = 0; i < pos; i++) { + switch (str[i]) { + case '\r': + if (str[i+1] == '\n') + i++; + /* fall through */ + case '\n': + (*line)++; + (*column) = 1; + break; + default: + (*column)++; + break; + } + } +} + +/** @memberof teco_string_t */ +gsize +teco_string_diff(const teco_string_t *a, const gchar *b, gsize b_len) +{ + gsize len = 0; + + while (len < a->len && len < b_len && + a->data[len] == b[len]) + len++; + + return len; +} + +/** @memberof teco_string_t */ +gsize +teco_string_casediff(const teco_string_t *a, const gchar *b, gsize b_len) +{ + gsize len = 0; + + while (len < a->len && len < b_len && + g_ascii_tolower(a->data[len]) == g_ascii_tolower(b[len])) + len++; + + return len; +} + +/** @memberof teco_string_t */ +gint +teco_string_cmp(const teco_string_t *a, const gchar *b, gsize b_len) +{ + for (guint i = 0; i < a->len; i++) { + if (i == b_len) + /* b is a prefix of a */ + return 1; + gint ret = (gint)a->data[i] - (gint)b[i]; + if (ret != 0) + /* a and b have a common prefix of length i */ + return ret; + } + + return a->len == b_len ? 0 : -1; +} + +/** @memberof teco_string_t */ +gint +teco_string_casecmp(const teco_string_t *a, const gchar *b, gsize b_len) +{ + for (guint i = 0; i < a->len; i++) { + if (i == b_len) + /* b is a prefix of a */ + return 1; + gint ret = (gint)g_ascii_tolower(a->data[i]) - (gint)g_ascii_tolower(b[i]); + if (ret != 0) + /* a and b have a common prefix of length i */ + return ret; + } + + return a->len == b_len ? 0 : -1; +} + +/** + * Find string after the last occurrence of any in a set of characters. + * + * @param str String to search through. + * @param chars Null-terminated set of characters. + * The null-byte itself is always considered part of the set. + * @return A null-terminated suffix of str or NULL. + * + * @memberof teco_string_t + */ +const gchar * +teco_string_last_occurrence(const teco_string_t *str, const gchar *chars) +{ + teco_string_t ret = *str; + + if (!ret.len) + return NULL; + + do { + gint i = teco_string_rindex(&ret, *chars); + if (i >= 0) { + ret.data += i+1; + ret.len -= i+1; + } + } while (*chars++); + + return ret.data; +} + +TECO_DEFINE_UNDO_CALL(teco_string_truncate, teco_string_t *, gsize); + +TECO_DEFINE_UNDO_OBJECT(cstring, gchar *, g_strdup, g_free); + +static inline teco_string_t +teco_string_copy(const teco_string_t str) +{ + teco_string_t ret; + teco_string_init(&ret, str.data, str.len); + return ret; +} + +#define DELETE(X) teco_string_clear(&(X)) +TECO_DEFINE_UNDO_OBJECT(string, teco_string_t, teco_string_copy, DELETE); +TECO_DEFINE_UNDO_OBJECT_OWN(string_own, teco_string_t, DELETE); +#undef DELETE |