Introduction
Introduction Statistics Contact Development Disclaimer Help
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 }
You are viewing proxied material from suckless.org. The copyright of proxied material belongs to its original authors. Any comments or complaints in relation to proxied material should be directed to the original authors of the content concerned. Please see the disclaimer for more details.