slackline.c (6196B)
1 /* 2 * Copyright (c) 2015-2023 Jan Klemkow <j.klemkow@wemelug.de> 3 * Copyright (c) 2022-2023 Tom Schwindl <schwindl@posteo.de> 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 WARRANTIES 10 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 11 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 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 OF 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-1)) != NULL) 172 sl_backspace(sl); 173 while (sl->rcur != 0 && strchr(IS_WORD_BREAK, *(sl->ptr-1)) == NULL) 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_len || 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 }