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