slackline.c - lchat - A line oriented chat front end for ii. | |
git clone git://git.suckless.org/lchat | |
Log | |
Files | |
Refs | |
README | |
--- | |
slackline.c (6196B) | |
--- | |
1 /* | |
2 * Copyright (c) 2015-2023 Jan Klemkow <[email protected]> | |
3 * Copyright (c) 2022-2023 Tom Schwindl <[email protected]> | |
4 * | |
5 * Permission to use, copy, modify, and distribute this software for any | |
6 * purpose with or without fee is hereby granted, provided that the above | |
7 * copyright notice and this permission notice appear in all copies. | |
8 * | |
9 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANT… | |
10 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF | |
11 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE F… | |
12 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES | |
13 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN | |
14 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT … | |
15 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. | |
16 */ | |
17 | |
18 #include <ctype.h> | |
19 #include <stdio.h> | |
20 #include <stdlib.h> | |
21 #include <string.h> | |
22 | |
23 #include <grapheme.h> | |
24 | |
25 #include "slackline_internals.h" | |
26 #include "slackline.h" | |
27 #include "util.h" | |
28 | |
29 /* CTRL+W: stop erasing if certain characters are reached. */ | |
30 #define IS_WORD_BREAK "\f\n\r\t\v (){}[]\\/#,.=-+|%$!@^&*" | |
31 | |
32 struct slackline * | |
33 sl_init(void) | |
34 { | |
35 char *mode = getenv("EDITOR"); | |
36 struct slackline *sl = malloc(sizeof *sl); | |
37 | |
38 if (sl == NULL) | |
39 return NULL; | |
40 | |
41 sl->bufsize = BUFSIZ; | |
42 if ((sl->buf = malloc(sl->bufsize)) == NULL) { | |
43 free(sl); | |
44 return NULL; | |
45 } | |
46 | |
47 memset(sl->ubuf, 0, sizeof(sl->ubuf)); | |
48 sl->ubuf_len = 0; | |
49 | |
50 sl_reset(sl); | |
51 | |
52 sl->mode = SL_DEFAULT; | |
53 if (mode != NULL) { | |
54 if (strcmp(mode, "emacs") == 0) | |
55 sl->mode = SL_EMACS; | |
56 else if (strcmp(mode, "vi") == 0) | |
57 sl->mode = SL_VI; | |
58 } | |
59 | |
60 return sl; | |
61 } | |
62 | |
63 void | |
64 sl_free(struct slackline *sl) | |
65 { | |
66 free(sl->buf); | |
67 free(sl); | |
68 } | |
69 | |
70 void | |
71 sl_reset(struct slackline *sl) | |
72 { | |
73 sl->buf[0] = '\0'; | |
74 sl->ptr = sl->buf; | |
75 sl->last = sl->buf; | |
76 | |
77 sl->bcur = 0; | |
78 sl->blen = 0; | |
79 sl->rcur = 0; | |
80 sl->rlen = 0; | |
81 | |
82 sl->esc = ESC_NONE; | |
83 sl->ubuf_len = 0; | |
84 } | |
85 | |
86 void | |
87 sl_mode(struct slackline *sl, enum mode mode) | |
88 { | |
89 sl->mode = mode; | |
90 } | |
91 | |
92 size_t | |
93 sl_postobyte(struct slackline *sl, size_t pos) | |
94 { | |
95 char *ptr = &sl->buf[0]; | |
96 size_t byte = 0; | |
97 | |
98 for (;pos > 0; pos--) | |
99 byte += grapheme_next_character_break_utf8(ptr+byte, | |
100 sl->blen-byte); | |
101 | |
102 return byte; | |
103 } | |
104 | |
105 char * | |
106 sl_postoptr(struct slackline *sl, size_t pos) | |
107 { | |
108 return &sl->buf[sl_postobyte(sl, pos)]; | |
109 } | |
110 | |
111 void | |
112 sl_backspace(struct slackline *sl) | |
113 { | |
114 char *ncur; | |
115 | |
116 if (sl->rcur == 0) | |
117 return; | |
118 | |
119 ncur = sl_postoptr(sl, sl->rcur - 1); | |
120 | |
121 if (sl->rcur < sl->rlen) | |
122 memmove(ncur, sl->ptr, sl->last - sl->ptr); | |
123 | |
124 sl->rcur--; | |
125 sl->rlen--; | |
126 sl->bcur = sl_postobyte(sl, sl->rcur); | |
127 sl->blen = sl_postobyte(sl, sl->rlen); | |
128 | |
129 sl->last -= sl->ptr - ncur; | |
130 *sl->last = '\0'; | |
131 | |
132 sl->ptr = ncur; | |
133 } | |
134 | |
135 void | |
136 sl_move(struct slackline *sl, enum direction dir) | |
137 { | |
138 switch (dir) { | |
139 case HOME: | |
140 sl->bcur = sl->rcur = 0; | |
141 sl->ptr = sl->buf; | |
142 return; | |
143 case END: | |
144 sl->rcur = sl->rlen; | |
145 break; | |
146 case RIGHT: | |
147 if (sl->rcur < sl->rlen) | |
148 sl->rcur++; | |
149 break; | |
150 case LEFT: | |
151 if (sl->rcur > 0) | |
152 sl->rcur--; | |
153 break; | |
154 } | |
155 | |
156 sl->bcur = sl_postobyte(sl, sl->rcur); | |
157 sl->ptr = sl->buf + sl->bcur; | |
158 } | |
159 | |
160 static void | |
161 sl_default(struct slackline *sl, int key) | |
162 { | |
163 switch (key) { | |
164 case ESC_KEY: | |
165 sl->esc = ESC; | |
166 break; | |
167 case CTRL_U: | |
168 sl_reset(sl); | |
169 break; | |
170 case CTRL_W: /* erase previous word */ | |
171 while (sl->rcur != 0 && strchr(IS_WORD_BREAK, *(sl->ptr-… | |
172 sl_backspace(sl); | |
173 while (sl->rcur != 0 && strchr(IS_WORD_BREAK, *(sl->ptr-… | |
174 sl_backspace(sl); | |
175 break; | |
176 case BACKSPACE: | |
177 case VT_BACKSPACE: | |
178 sl_backspace(sl); | |
179 break; | |
180 default: | |
181 break; | |
182 } | |
183 } | |
184 | |
185 static int | |
186 sl_esc(struct slackline *sl, int key) | |
187 { | |
188 /* handle escape sequences */ | |
189 switch (sl->esc) { | |
190 case ESC_NONE: | |
191 break; | |
192 case ESC: | |
193 sl->esc = key == '[' ? ESC_BRACKET : ESC_NONE; | |
194 return 1; | |
195 case ESC_BRACKET: | |
196 switch (key) { | |
197 case 'A': /* up */ | |
198 case 'B': /* down */ | |
199 break; | |
200 case 'C': /* right */ | |
201 sl_move(sl, RIGHT); | |
202 break; | |
203 case 'D': /* left */ | |
204 sl_move(sl, LEFT); | |
205 break; | |
206 case 'H': /* Home */ | |
207 sl_move(sl, HOME); | |
208 break; | |
209 case 'F': /* End */ | |
210 sl_move(sl, END); | |
211 break; | |
212 case 'P': /* delete */ | |
213 if (sl->rcur == sl->rlen) | |
214 break; | |
215 sl_move(sl, RIGHT); | |
216 sl_backspace(sl); | |
217 break; | |
218 case '0': | |
219 case '1': | |
220 case '2': | |
221 case '3': | |
222 case '4': | |
223 case '5': | |
224 case '6': | |
225 case '7': | |
226 case '8': | |
227 case '9': | |
228 sl->nummod = key; | |
229 sl->esc = ESC_BRACKET_NUM; | |
230 return 1; | |
231 } | |
232 sl->esc = ESC_NONE; | |
233 return 1; | |
234 case ESC_BRACKET_NUM: | |
235 switch(key) { | |
236 case '~': | |
237 switch(sl->nummod) { | |
238 case '1': /* Home */ | |
239 case '7': | |
240 sl_move(sl, HOME); | |
241 break; | |
242 case '4': /* End */ | |
243 case '8': | |
244 sl_move(sl, END); | |
245 break; | |
246 case '3': /* Delete */ | |
247 if (sl->rcur == sl->rlen) | |
248 break; | |
249 sl_move(sl, RIGHT); | |
250 sl_backspace(sl); | |
251 break; | |
252 } | |
253 sl->esc = ESC_NONE; | |
254 return 1; | |
255 } | |
256 } | |
257 | |
258 return 0; | |
259 } | |
260 | |
261 int | |
262 sl_keystroke(struct slackline *sl, int key) | |
263 { | |
264 uint_least32_t cp; | |
265 | |
266 if (sl == NULL || sl->rlen < sl->rcur) | |
267 return -1; | |
268 if (sl_esc(sl, key)) | |
269 return 0; | |
270 if (!iscntrl((unsigned char) key)) | |
271 goto compose; | |
272 | |
273 switch (sl->mode) { | |
274 case SL_DEFAULT: | |
275 sl_default(sl, key); | |
276 break; | |
277 case SL_EMACS: | |
278 sl_default(sl, key); | |
279 sl_emacs(sl, key); | |
280 break; | |
281 case SL_VI: | |
282 /* TODO: implement vi-mode */ | |
283 break; | |
284 } | |
285 return 0; | |
286 | |
287 compose: | |
288 /* byte-wise composing of UTF-8 runes */ | |
289 sl->ubuf[sl->ubuf_len++] = key; | |
290 if (grapheme_decode_utf8(sl->ubuf, sl->ubuf_len, &cp) > sl->ubuf… | |
291 cp == GRAPHEME_INVALID_CODEPOINT) | |
292 return 0; | |
293 | |
294 if (sl->blen + sl->ubuf_len >= sl->bufsize) { | |
295 char *nbuf; | |
296 | |
297 if ((nbuf = realloc(sl->buf, sl->bufsize * 2)) == NULL) | |
298 return -1; | |
299 | |
300 sl->ptr = nbuf + (sl->ptr - sl->buf); | |
301 sl->last = nbuf + (sl->last - sl->buf); | |
302 sl->buf = nbuf; | |
303 sl->bufsize *= 2; | |
304 } | |
305 | |
306 /* add character to buffer */ | |
307 if (sl->rcur < sl->rlen) { /* insert into buffer */ | |
308 char *cur = sl_postoptr(sl, sl->rcur); | |
309 char *end = sl_postoptr(sl, sl->rlen); | |
310 char *ncur = cur + sl->ubuf_len; | |
311 | |
312 memmove(ncur, cur, end - cur); | |
313 } | |
314 | |
315 memcpy(sl_postoptr(sl, sl->rcur), sl->ubuf, sl->ubuf_len); | |
316 | |
317 sl->ptr += sl->ubuf_len; | |
318 sl->last += sl->ubuf_len; | |
319 sl->bcur += sl->ubuf_len; | |
320 sl->blen += sl->ubuf_len; | |
321 sl->ubuf_len = 0; | |
322 | |
323 sl->rcur++; | |
324 sl->rlen++; | |
325 | |
326 *sl->last = '\0'; | |
327 | |
328 return 0; | |
329 } |