]> git.ipfire.org Git - thirdparty/util-linux.git/blob - lib/mbsedit.c
Merge branch 'clock' of https://github.com/t-8ch/util-linux
[thirdparty/util-linux.git] / lib / mbsedit.c
1 /*
2 * SPDX-License-Identifier: LGPL-2.1-or-later
3 *
4 * Very simple multibyte buffer editor. Allows to maintaine the current
5 * position in the string, add and remove chars on the current position.
6 *
7 * This file may be distributed under the terms of the
8 * GNU Lesser General Public License.
9 *
10 * Copyright (C) 2017 Karel Zak <kzak@redhat.com>
11 */
12 #include <stdlib.h>
13 #include <string.h>
14 #include <errno.h>
15 #include <stdio.h>
16
17 #include "mbsalign.h"
18 #include "mbsedit.h"
19
20 struct mbs_editor *mbs_new_edit(char *buf, size_t bufsz, size_t ncells)
21 {
22 struct mbs_editor *edit = calloc(1, sizeof(*edit));
23
24 if (edit) {
25 edit->buf = buf;
26 edit->max_bytes = bufsz;
27 edit->max_cells = ncells;
28 edit->cur_cells = mbs_safe_width(buf);
29 edit->cur_bytes = strlen(buf);
30 }
31 return edit;
32 }
33
34 char *mbs_free_edit(struct mbs_editor *edit)
35 {
36 char *ret = edit ? edit->buf : NULL;
37
38 free(edit);
39 return ret;
40 }
41
42 static size_t mbs_next(const char *str, size_t *ncells)
43 {
44 #ifdef HAVE_WIDECHAR
45 wchar_t wc;
46 size_t n = 0;
47
48 if (!str || !*str)
49 return 0;
50
51 n = mbrtowc(&wc, str, MB_CUR_MAX, NULL);
52 *ncells = wcwidth(wc);
53 return n;
54 #else
55 if (!str || !*str)
56 return 0;
57 *ncells = 1;
58 return 1;
59 #endif
60 }
61
62 static size_t mbs_prev(const char *start, const char *end, size_t *ncells)
63 {
64 #ifdef HAVE_WIDECHAR
65 wchar_t wc = 0;
66 const char *p, *prev;
67 size_t n = 0;
68
69 if (!start || !end || start == end || !*start)
70 return 0;
71
72 prev = p = start;
73 while (p < end) {
74 n = mbrtowc(&wc, p, MB_CUR_MAX, NULL);
75 prev = p;
76
77 if (n == (size_t) -1 || n == (size_t) -2)
78 p++;
79 else
80 p += n;
81 }
82
83 if (prev == end)
84 return 0;
85 *ncells = wcwidth(wc);
86 return n;
87 #else
88 if (!start || !end || start == end || !*start)
89 return 0;
90 *ncells = 1;
91 return 1;
92 #endif
93 }
94
95 int mbs_edit_goto(struct mbs_editor *edit, int where)
96 {
97 switch (where) {
98 case MBS_EDIT_LEFT:
99 if (edit->cursor == 0)
100 return 1;
101 else {
102 size_t n, cells;
103 n = mbs_prev(edit->buf, edit->buf + edit->cursor, &cells);
104 if (n) {
105 edit->cursor -= n;
106 edit->cursor_cells -= cells;
107 }
108 }
109 break;
110 case MBS_EDIT_RIGHT:
111 if (edit->cursor_cells >= edit->cur_cells)
112 return 1;
113 else {
114 size_t n, cells;
115 n = mbs_next(edit->buf + edit->cursor, &cells);
116 if (n) {
117 edit->cursor += n;
118 edit->cursor_cells += cells;
119 }
120 }
121 break;
122 case MBS_EDIT_HOME:
123 edit->cursor = 0;
124 edit->cursor_cells = 0;
125 break;
126 case MBS_EDIT_END:
127 edit->cursor = edit->cur_bytes;
128 edit->cursor_cells = edit->cur_cells;
129 break;
130 default:
131 return -EINVAL;
132 }
133
134 return 0;
135 }
136
137 /* Remove next MB from @str, returns number of removed bytes */
138 static size_t remove_next(char *str, size_t *ncells)
139 {
140 /* all in bytes! */
141 size_t bytes, move_bytes, n;
142
143 n = mbs_next(str, ncells);
144 bytes = strlen(str);
145 move_bytes = bytes - n;
146
147 memmove(str, str + n, move_bytes);
148 str[bytes - n] = '\0';
149 return n;
150 }
151
152 static size_t mbs_insert(char *str, wint_t c, size_t *ncells)
153 {
154 /* all in bytes! */
155 size_t n = 1, bytes;
156 char *in;
157
158 #ifdef HAVE_WIDECHAR
159 wchar_t wc = (wchar_t) c;
160 char in_buf[MB_CUR_MAX];
161
162 n = wctomb(in_buf, wc);
163 if (n == (size_t) -1)
164 return n;
165 *ncells = wcwidth(wc);
166 in = in_buf;
167 #else
168 *ncells = 1;
169 in = (char *) &c;
170 #endif
171 bytes = strlen(str);
172
173 memmove(str + n, str, bytes);
174 memcpy(str, in, n);
175 str[bytes + n] = '\0';
176 return n;
177 }
178
179 static int mbs_edit_remove(struct mbs_editor *edit)
180 {
181 size_t n, ncells;
182
183 if (edit->cur_cells == 0 || edit->cursor >= edit->cur_bytes)
184 return 1;
185
186 n = remove_next(edit->buf + edit->cursor, &ncells);
187 if (n == (size_t)-1)
188 return 1;
189
190 edit->cur_bytes -= n;
191 edit->cur_cells = mbs_safe_width(edit->buf);
192 return 0;
193 }
194
195 int mbs_edit_delete(struct mbs_editor *edit)
196 {
197 if (edit->cursor >= edit->cur_bytes
198 && mbs_edit_goto(edit, MBS_EDIT_LEFT) == 1)
199 return 1;
200
201 return mbs_edit_remove(edit);
202 }
203
204 int mbs_edit_backspace(struct mbs_editor *edit)
205 {
206 if (mbs_edit_goto(edit, MBS_EDIT_LEFT) == 0)
207 return mbs_edit_remove(edit);
208 return 1;
209 }
210
211 int mbs_edit_insert(struct mbs_editor *edit, wint_t c)
212 {
213 size_t n, ncells;
214
215 if (edit->cur_bytes + MB_CUR_MAX > edit->max_bytes)
216 return 1;
217
218 n = mbs_insert(edit->buf + edit->cursor, c, &ncells);
219 if (n == (size_t)-1)
220 return 1;
221
222 edit->cursor += n;
223 edit->cursor_cells += ncells;
224 edit->cur_bytes += n;
225 edit->cur_cells = mbs_safe_width(edit->buf);
226 return 0;
227 }