]>
Commit | Line | Data |
---|---|---|
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 | ||
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; | |
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 | ||
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 | } |