]> git.ipfire.org Git - thirdparty/util-linux.git/blame - lib/mbsedit.c
lscpu: make clang analyzer happy
[thirdparty/util-linux.git] / lib / mbsedit.c
CommitLineData
299ef2c4
KZ
1/*
2 * Very simple multibyte buffer editor. Allows to maintaine the current
c6b0cbdd 3 * position in the string, add and remove chars on the current position.
299ef2c4
KZ
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
18struct 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
32char *mbs_free_edit(struct mbs_editor *edit)
33{
34 char *ret = edit ? edit->buf : NULL;
35
36 free(edit);
37 return ret;
38}
39
40static 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
60static 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
93int 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 */
136static 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
150static 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 = (char *) &c;
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 *ncells = wcwidth(wc);
162 in = in_buf;
163#else
164 *ncells = 1;
165#endif
166 bytes = strlen(str);
167
168 memmove(str + n, str, bytes);
169 memcpy(str, in, n);
170 str[bytes + n] = '\0';
171 return n;
172}
173
174static int mbs_edit_remove(struct mbs_editor *edit)
175{
176 size_t n, ncells;
177
178 if (edit->cur_cells == 0 || edit->cursor >= edit->cur_bytes)
179 return 1;
180
181 n = remove_next(edit->buf + edit->cursor, &ncells);
182 if (n == (size_t)-1)
183 return 1;
184
185 edit->cur_bytes -= n;
186 edit->cur_cells = mbs_safe_width(edit->buf);
187 return 0;
188}
189
190int mbs_edit_delete(struct mbs_editor *edit)
191{
192 if (edit->cursor >= edit->cur_bytes
193 && mbs_edit_goto(edit, MBS_EDIT_LEFT) == 1)
194 return 1;
195
196 return mbs_edit_remove(edit);
197}
198
199int mbs_edit_backspace(struct mbs_editor *edit)
200{
201 if (mbs_edit_goto(edit, MBS_EDIT_LEFT) == 0)
202 return mbs_edit_remove(edit);
203 return 1;
204}
205
206int mbs_edit_insert(struct mbs_editor *edit, wint_t c)
207{
208 size_t n, ncells;
209
210 if (edit->cur_bytes + MB_CUR_MAX > edit->max_bytes)
211 return 1;
212
213 n = mbs_insert(edit->buf + edit->cursor, c, &ncells);
214 if (n == (size_t)-1)
215 return 1;
216
217 edit->cursor += n;
218 edit->cursor_cells += ncells;
219 edit->cur_bytes += n;
220 edit->cur_cells = mbs_safe_width(edit->buf);
221 return 0;
222}