]> git.ipfire.org Git - thirdparty/util-linux.git/commitdiff
lib/mbsedit: add simple buffer editor
authorKarel Zak <kzak@redhat.com>
Thu, 16 Feb 2017 12:04:26 +0000 (13:04 +0100)
committerKarel Zak <kzak@redhat.com>
Thu, 16 Feb 2017 12:47:44 +0000 (13:47 +0100)
Signed-off-by: Karel Zak <kzak@redhat.com>
include/mbsedit.h [new file with mode: 0644]
lib/Makemodule.am
lib/mbsedit.c [new file with mode: 0644]

diff --git a/include/mbsedit.h b/include/mbsedit.h
new file mode 100644 (file)
index 0000000..8d1c6c2
--- /dev/null
@@ -0,0 +1,32 @@
+#ifndef UTIL_LINUX_MBSEDIT_H
+# define UTIL_LINUX_MBSEDIT_H
+
+#include "mbsalign.h"
+#include "widechar.h"
+
+struct mbs_editor {
+       char    *buf;           /* buffer */
+       size_t  max_bytes;      /* size of the buffer */
+       size_t  max_cells;      /* maximal allowed number of cells */
+       size_t  cur_cells;      /* number of cells to print the buffer */
+       size_t  cur_bytes;      /* number of chars in bytes */
+       size_t  cursor;         /* cursor position in bytes */
+       size_t  cursor_cells;   /* cursor position in cells */
+};
+
+enum {
+       MBS_EDIT_LEFT,
+       MBS_EDIT_RIGHT,
+       MBS_EDIT_END,
+       MBS_EDIT_HOME
+};
+
+struct mbs_editor *mbs_new_edit(char *buf, size_t bufsz, size_t ncells);
+char *mbs_free_edit(struct mbs_editor *edit);
+
+int mbs_edit_goto(struct mbs_editor *edit, int where);
+int mbs_edit_delete(struct mbs_editor *edit);
+int mbs_edit_backspace(struct mbs_editor *edit);
+int mbs_edit_insert(struct mbs_editor *edit, wint_t c);
+
+#endif /* UTIL_LINUX_MBSEDIT_H */
index 209a51dc517e7750f11923e4609266143c5c30f8..d20a9aca260fd5e6b712a2fc4c05fea9704fd258 100644 (file)
@@ -13,6 +13,7 @@ libcommon_la_SOURCES = \
        lib/mangle.c \
        lib/match.c \
        lib/mbsalign.c \
+       lib/mbsedit.c\
        lib/md5.c \
        lib/pager.c \
        lib/path.c \
diff --git a/lib/mbsedit.c b/lib/mbsedit.c
new file mode 100644 (file)
index 0000000..2f322aa
--- /dev/null
@@ -0,0 +1,222 @@
+/*
+ * Very simple multibyte buffer editor. Allows to maintaine the current
+ * possition in the string, add and remove chars on the current possition.
+ *
+ * This file may be distributed under the terms of the
+ * GNU Lesser General Public License.
+ *
+ * Copyright (C) 2017 Karel Zak <kzak@redhat.com>
+ */
+#include <stdlib.h>
+#include <string.h>
+#include <errno.h>
+#include <stdio.h>
+
+#include "mbsalign.h"
+#include "mbsedit.h"
+
+struct mbs_editor *mbs_new_edit(char *buf, size_t bufsz, size_t ncells)
+{
+       struct mbs_editor *edit = calloc(1, sizeof(*edit));
+
+       if (edit) {
+               edit->buf = buf;
+               edit->max_bytes = bufsz;
+               edit->max_cells = ncells;
+               edit->cur_cells = mbs_safe_width(buf);
+               edit->cur_bytes = strlen(buf);
+       }
+       return edit;
+}
+
+char *mbs_free_edit(struct mbs_editor *edit)
+{
+       char *ret = edit ? edit->buf : NULL;
+
+       free(edit);
+       return ret;
+}
+
+static size_t mbs_next(const char *str, size_t *ncells)
+{
+#ifdef HAVE_WIDECHAR
+       wchar_t wc;
+       size_t n = 0;
+
+       if (!str || !*str)
+               return 0;
+
+       n = mbrtowc(&wc, str, MB_CUR_MAX, NULL);
+       *ncells = wcwidth(wc);
+       return n;
+#else
+       if (!str || !*str)
+               return 0;
+       *ncells = 1;
+       return 1;
+#endif
+}
+
+static size_t mbs_prev(const char *start, const char *end, size_t *ncells)
+{
+#ifdef HAVE_WIDECHAR
+       wchar_t wc = 0;
+       const char *p, *prev;
+       size_t n = 0;
+
+       if (!start || !end || start == end || !*start)
+               return 0;
+
+       prev = p = start;
+       while (p < end) {
+               n = mbrtowc(&wc, p, MB_CUR_MAX, NULL);
+               prev = p;
+
+               if (n == (size_t) -1 || n == (size_t) -2)
+                       p++;
+               else
+                       p += n;
+       }
+
+       if (prev == end)
+               return 0;
+       *ncells = wcwidth(wc);
+       return n;
+#else
+       if (!start || !end || start == end || !*start)
+               return 0;
+       *ncells = 1;
+       return 1;
+#endif
+}
+
+int mbs_edit_goto(struct mbs_editor *edit, int where)
+{
+       switch (where) {
+       case MBS_EDIT_LEFT:
+               if (edit->cursor == 0)
+                       return 1;
+               else {
+                       size_t n, cells;
+                       n = mbs_prev(edit->buf, edit->buf + edit->cursor, &cells);
+                       if (n) {
+                               edit->cursor -= n;
+                               edit->cursor_cells -= cells;
+                       }
+               }
+               break;
+       case MBS_EDIT_RIGHT:
+               if (edit->cursor_cells >= edit->cur_cells)
+                       return 1;
+               else {
+                       size_t n, cells;
+                       n = mbs_next(edit->buf + edit->cursor, &cells);
+                       if (n) {
+                               edit->cursor += n;
+                               edit->cursor_cells += cells;
+                       }
+               }
+               break;
+       case MBS_EDIT_HOME:
+               edit->cursor = 0;
+               edit->cursor_cells = 0;
+               break;
+       case MBS_EDIT_END:
+               edit->cursor = edit->cur_bytes;
+               edit->cursor_cells = edit->cur_cells;
+               break;
+       default:
+               return -EINVAL;
+       }
+
+       return 0;
+}
+
+/* Remove next MB from @str, returns number of removed bytes */
+static size_t remove_next(char *str, size_t *ncells)
+{
+       /* all in bytes! */
+       size_t bytes, move_bytes, n;
+
+       n          = mbs_next(str, ncells);
+       bytes      = strlen(str);
+       move_bytes = bytes - n;
+
+       memmove(str, str + n, move_bytes);
+       str[bytes - n] = '\0';
+       return n;
+}
+
+static size_t mbs_insert(char *str, wint_t c, size_t *ncells)
+{
+       /* all in bytes! */
+       size_t n = 1, bytes;
+       char *in = (char *) &c;
+
+#ifdef HAVE_WIDECHAR
+       wchar_t wc = (wchar_t) c;
+       char in_buf[MB_CUR_MAX];
+
+       n = wctomb(in_buf, wc);
+       *ncells = wcwidth(wc);
+       in = in_buf;
+#else
+       *ncells = 1;
+#endif
+       bytes       = strlen(str);
+
+       memmove(str + n, str, bytes);
+       memcpy(str, in, n);
+       str[bytes + n] = '\0';
+       return n;
+}
+
+static int mbs_edit_remove(struct mbs_editor *edit)
+{
+       size_t n, ncells;
+
+       if (edit->cur_cells == 0 || edit->cursor >= edit->cur_bytes)
+               return 1;
+
+       n = remove_next(edit->buf + edit->cursor, &ncells);
+       if (n == (size_t)-1)
+               return 1;
+
+       edit->cur_bytes -= n;
+       edit->cur_cells = mbs_safe_width(edit->buf);
+       return 0;
+}
+
+int mbs_edit_delete(struct mbs_editor *edit)
+{
+       if (edit->cursor >= edit->cur_bytes
+           && mbs_edit_goto(edit, MBS_EDIT_LEFT) == 1)
+               return 1;
+
+       return mbs_edit_remove(edit);
+}
+
+int mbs_edit_backspace(struct mbs_editor *edit)
+{
+       if (mbs_edit_goto(edit, MBS_EDIT_LEFT) == 0)
+               return mbs_edit_remove(edit);
+       return 1;
+}
+
+int mbs_edit_insert(struct mbs_editor *edit, wint_t c)
+{
+       size_t n, ncells;
+
+       if (edit->cur_bytes + MB_CUR_MAX > edit->max_bytes)
+               return 1;
+
+       n = mbs_insert(edit->buf + edit->cursor, c, &ncells);
+       if (n == (size_t)-1)
+               return 1;
+
+       edit->cursor += n;
+       edit->cursor_cells += ncells;
+       edit->cur_bytes += n;
+       edit->cur_cells = mbs_safe_width(edit->buf);
+       return 0;
+}