]> git.ipfire.org Git - thirdparty/util-linux.git/blob - lib/mbsedit.c
lib/colors: correct documentation of colors_add_scheme()
[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 in = malloc(MB_CUR_MAX);
161 if (!in)
162 return -1;
163
164 n = wctomb(in, wc);
165 if (n == (size_t) -1)
166 goto out;
167 *ncells = wcwidth(wc);
168 #else
169 *ncells = 1;
170 in = (char *) &c;
171 #endif
172 bytes = strlen(str);
173
174 memmove(str + n, str, bytes);
175 memcpy(str, in, n);
176 str[bytes + n] = '\0';
177 out:
178 #ifdef HAVE_WIDECHAR
179 free(in);
180 #endif
181 return n;
182 }
183
184 static int mbs_edit_remove(struct mbs_editor *edit)
185 {
186 size_t n, ncells;
187
188 if (edit->cur_cells == 0 || edit->cursor >= edit->cur_bytes)
189 return 1;
190
191 n = remove_next(edit->buf + edit->cursor, &ncells);
192 if (n == (size_t)-1)
193 return 1;
194
195 edit->cur_bytes -= n;
196 edit->cur_cells = mbs_safe_width(edit->buf);
197 return 0;
198 }
199
200 int mbs_edit_delete(struct mbs_editor *edit)
201 {
202 if (edit->cursor >= edit->cur_bytes
203 && mbs_edit_goto(edit, MBS_EDIT_LEFT) == 1)
204 return 1;
205
206 return mbs_edit_remove(edit);
207 }
208
209 int mbs_edit_backspace(struct mbs_editor *edit)
210 {
211 if (mbs_edit_goto(edit, MBS_EDIT_LEFT) == 0)
212 return mbs_edit_remove(edit);
213 return 1;
214 }
215
216 int mbs_edit_insert(struct mbs_editor *edit, wint_t c)
217 {
218 size_t n, ncells;
219
220 if (edit->cur_bytes + MB_CUR_MAX > edit->max_bytes)
221 return 1;
222
223 n = mbs_insert(edit->buf + edit->cursor, c, &ncells);
224 if (n == (size_t)-1)
225 return 1;
226
227 edit->cursor += n;
228 edit->cursor_cells += ncells;
229 edit->cur_bytes += n;
230 edit->cur_cells = mbs_safe_width(edit->buf);
231 return 0;
232 }