]> git.ipfire.org Git - thirdparty/util-linux.git/blame - lib/mbsedit.c
rev: be careful with close()
[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;
c3ae7854 154 char *in;
299ef2c4
KZ
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);
4bb91e84
KZ
161 if (n == (size_t) -1)
162 return n;
299ef2c4
KZ
163 *ncells = wcwidth(wc);
164 in = in_buf;
165#else
166 *ncells = 1;
c3ae7854 167 in = (char *) &c;
299ef2c4
KZ
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
177static 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
193int 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
202int 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
209int 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}