1 /* $OpenBSD: kbd.c,v 1.37 2023/03/08 04:43:11 guenther Exp $ */ 2 3 /* This file is in the public domain. */ 4 5 /* 6 * Terminal independent keyboard handling. 7 */ 8 9 #include <sys/queue.h> 10 #include <signal.h> 11 #include <stdio.h> 12 13 #include "def.h" 14 #include "kbd.h" 15 #include "key.h" 16 #include "macro.h" 17 18 #ifdef MGLOG 19 #include "log.h" 20 #endif 21 22 #define METABIT 0x80 23 24 #define PROMPTL 80 25 char prompt[PROMPTL] = "", *promptp = prompt; 26 27 static int mgwrap(PF, int, int); 28 29 static int use_metakey = TRUE; 30 static int pushed = FALSE; 31 static int pushedc; 32 33 struct map_element *ele; 34 struct key key; 35 int rptcount; 36 37 /* 38 * Toggle the value of use_metakey 39 */ 40 int 41 do_meta(int f, int n) 42 { 43 if (f & FFARG) 44 use_metakey = n > 0; 45 else 46 use_metakey = !use_metakey; 47 ewprintf("Meta keys %sabled", use_metakey ? "en" : "dis"); 48 return (TRUE); 49 } 50 51 static int bs_map = 0; 52 53 /* 54 * Toggle backspace mapping 55 */ 56 int 57 bsmap(int f, int n) 58 { 59 if (f & FFARG) 60 bs_map = n > 0; 61 else 62 bs_map = !bs_map; 63 ewprintf("Backspace mapping %sabled", bs_map ? "en" : "dis"); 64 return (TRUE); 65 } 66 67 void 68 ungetkey(int c) 69 { 70 if (use_metakey && pushed && c == CCHR('[')) 71 pushedc |= METABIT; 72 else 73 pushedc = c; 74 pushed = TRUE; 75 } 76 77 int 78 getkey(int flag) 79 { 80 int c; 81 82 if (flag && !pushed) { 83 if (prompt[0] != '\0' && ttwait(2000)) { 84 /* avoid problems with % */ 85 ewprintf("%s", prompt); 86 /* put the cursor back */ 87 update(CMODE); 88 epresf = KCLEAR; 89 } 90 if (promptp > prompt) 91 *(promptp - 1) = ' '; 92 } 93 if (pushed) { 94 c = pushedc; 95 pushed = FALSE; 96 } else 97 c = ttgetc(); 98 99 if (bs_map) { 100 if (c == CCHR('H')) 101 c = CCHR('?'); 102 else if (c == CCHR('?')) 103 c = CCHR('H'); 104 } 105 if (use_metakey && (c & METABIT)) { 106 pushedc = c & ~METABIT; 107 pushed = TRUE; 108 c = CCHR('['); 109 } 110 if (flag && promptp < &prompt[PROMPTL - 5]) { 111 promptp = getkeyname(promptp, 112 sizeof(prompt) - (promptp - prompt) - 1, c); 113 *promptp++ = '-'; 114 *promptp = '\0'; 115 } 116 return (c); 117 } 118 119 /* 120 * doscan scans a keymap for a keyboard character and returns a pointer 121 * to the function associated with that character. Sets ele to the 122 * keymap element the keyboard was found in as a side effect. 123 */ 124 PF 125 doscan(KEYMAP *map, int c, KEYMAP **newmap) 126 { 127 struct map_element *elec = &map->map_element[0]; 128 struct map_element *last = &map->map_element[map->map_num]; 129 PF ret; 130 131 while (elec < last && c > elec->k_num) 132 elec++; 133 134 /* used by prefix and binding code */ 135 ele = elec; 136 if (elec >= last || c < elec->k_base) 137 ret = map->map_default; 138 else 139 ret = elec->k_funcp[c - elec->k_base]; 140 if (ret == NULL && newmap != NULL) 141 *newmap = elec->k_prefmap; 142 143 return (ret); 144 } 145 146 int 147 doin(void) 148 { 149 KEYMAP *curmap; 150 PF funct; 151 152 *(promptp = prompt) = '\0'; 153 curmap = curbp->b_modes[curbp->b_nmodes]->p_map; 154 key.k_count = 0; 155 while ((funct = doscan(curmap, (key.k_chars[key.k_count++] = 156 getkey(TRUE)), &curmap)) == NULL) 157 /* nothing */; 158 159 #ifdef MGLOG 160 if (!mglog(funct, curmap)) 161 ewprintf("Problem with logging"); 162 #endif 163 164 if (macrodef && macrocount < MAXMACRO) 165 macro[macrocount++].m_funct = funct; 166 167 return (mgwrap(funct, 0, 1)); 168 } 169 170 int 171 rescan(int f, int n) 172 { 173 int c; 174 KEYMAP *curmap; 175 int i; 176 PF fp = NULL; 177 int md = curbp->b_nmodes; 178 179 for (;;) { 180 if (ISUPPER(key.k_chars[key.k_count - 1])) { 181 c = TOLOWER(key.k_chars[key.k_count - 1]); 182 curmap = curbp->b_modes[md]->p_map; 183 for (i = 0; i < key.k_count - 1; i++) { 184 if ((fp = doscan(curmap, (key.k_chars[i]), 185 &curmap)) != NULL) 186 break; 187 } 188 if (fp == NULL) { 189 if ((fp = doscan(curmap, c, NULL)) == NULL) 190 while ((fp = doscan(curmap, 191 key.k_chars[key.k_count++] = 192 getkey(TRUE), &curmap)) == NULL) 193 /* nothing */; 194 if (fp != rescan) { 195 if (macrodef && macrocount <= MAXMACRO) 196 macro[macrocount - 1].m_funct 197 = fp; 198 return (mgwrap(fp, f, n)); 199 } 200 } 201 } 202 /* try previous mode */ 203 if (--md < 0) 204 return (ABORT); 205 curmap = curbp->b_modes[md]->p_map; 206 for (i = 0; i < key.k_count; i++) { 207 if ((fp = doscan(curmap, (key.k_chars[i]), &curmap)) != NULL) 208 break; 209 } 210 if (fp == NULL) { 211 while ((fp = doscan(curmap, key.k_chars[i++] = 212 getkey(TRUE), &curmap)) == NULL) 213 /* nothing */; 214 key.k_count = i; 215 } 216 if (fp != rescan && i >= key.k_count - 1) { 217 if (macrodef && macrocount <= MAXMACRO) 218 macro[macrocount - 1].m_funct = fp; 219 return (mgwrap(fp, f, n)); 220 } 221 } 222 } 223 224 int 225 universal_argument(int f, int n) 226 { 227 KEYMAP *curmap; 228 PF funct; 229 int c, nn = 4; 230 231 if (f & FFUNIV) 232 nn *= n; 233 for (;;) { 234 key.k_chars[0] = c = getkey(TRUE); 235 key.k_count = 1; 236 if (c == '-') 237 return (negative_argument(f, nn)); 238 if (c >= '0' && c <= '9') 239 return (digit_argument(f, nn)); 240 curmap = curbp->b_modes[curbp->b_nmodes]->p_map; 241 while ((funct = doscan(curmap, c, &curmap)) == NULL) { 242 key.k_chars[key.k_count++] = c = getkey(TRUE); 243 } 244 if (funct != universal_argument) { 245 if (macrodef && macrocount < MAXMACRO - 1) { 246 if (f & FFARG) 247 macrocount--; 248 macro[macrocount++].m_count = nn; 249 macro[macrocount++].m_funct = funct; 250 } 251 return (mgwrap(funct, FFUNIV, nn)); 252 } 253 nn <<= 2; 254 } 255 } 256 257 int 258 digit_argument(int f, int n) 259 { 260 KEYMAP *curmap; 261 PF funct; 262 int nn, c; 263 264 nn = key.k_chars[key.k_count - 1] - '0'; 265 for (;;) { 266 c = getkey(TRUE); 267 if (c < '0' || c > '9') 268 break; 269 nn *= 10; 270 nn += c - '0'; 271 } 272 key.k_chars[0] = c; 273 key.k_count = 1; 274 curmap = curbp->b_modes[curbp->b_nmodes]->p_map; 275 while ((funct = doscan(curmap, c, &curmap)) == NULL) { 276 key.k_chars[key.k_count++] = c = getkey(TRUE); 277 } 278 if (macrodef && macrocount < MAXMACRO - 1) { 279 if (f & FFARG) 280 macrocount--; 281 else 282 macro[macrocount - 1].m_funct = universal_argument; 283 macro[macrocount++].m_count = nn; 284 macro[macrocount++].m_funct = funct; 285 } 286 return (mgwrap(funct, FFOTHARG, nn)); 287 } 288 289 int 290 negative_argument(int f, int n) 291 { 292 KEYMAP *curmap; 293 PF funct; 294 int c; 295 int nn = 0; 296 297 for (;;) { 298 c = getkey(TRUE); 299 if (c < '0' || c > '9') 300 break; 301 nn *= 10; 302 nn += c - '0'; 303 } 304 if (nn) 305 nn = -nn; 306 else 307 nn = -n; 308 key.k_chars[0] = c; 309 key.k_count = 1; 310 curmap = curbp->b_modes[curbp->b_nmodes]->p_map; 311 while ((funct = doscan(curmap, c, &curmap)) == NULL) { 312 key.k_chars[key.k_count++] = c = getkey(TRUE); 313 } 314 if (macrodef && macrocount < MAXMACRO - 1) { 315 if (f & FFARG) 316 macrocount--; 317 else 318 macro[macrocount - 1].m_funct = universal_argument; 319 macro[macrocount++].m_count = nn; 320 macro[macrocount++].m_funct = funct; 321 } 322 return (mgwrap(funct, FFNEGARG, nn)); 323 } 324 325 /* 326 * Insert a character. While defining a macro, create a "LINE" containing 327 * all inserted characters. 328 */ 329 int 330 selfinsert(int f, int n) 331 { 332 struct line *lp; 333 int c; 334 int count; 335 336 if (n < 0) 337 return (FALSE); 338 if (n == 0) 339 return (TRUE); 340 c = key.k_chars[key.k_count - 1]; 341 342 if (macrodef && macrocount < MAXMACRO) { 343 if (f & FFARG) 344 macrocount -= 2; 345 346 /* last command was insert -- tack on the end */ 347 if (lastflag & CFINS) { 348 macrocount--; 349 /* Ensure the line can handle the new characters */ 350 if (maclcur->l_size < maclcur->l_used + n) { 351 if (lrealloc(maclcur, maclcur->l_used + n) == 352 FALSE) 353 return (FALSE); 354 } 355 maclcur->l_used += n; 356 /* Copy in the new data */ 357 for (count = maclcur->l_used - n; 358 count < maclcur->l_used; count++) 359 maclcur->l_text[count] = c; 360 } else { 361 macro[macrocount - 1].m_funct = insert; 362 if ((lp = lalloc(n)) == NULL) 363 return (FALSE); 364 lp->l_bp = maclcur; 365 lp->l_fp = maclcur->l_fp; 366 maclcur->l_fp = lp; 367 maclcur = lp; 368 for (count = 0; count < n; count++) 369 lp->l_text[count] = c; 370 } 371 thisflag |= CFINS; 372 } 373 if (c == *curbp->b_nlchr) { 374 do { 375 count = lnewline(); 376 } while (--n && count == TRUE); 377 return (count); 378 } 379 380 /* overwrite mode */ 381 if (curbp->b_flag & BFOVERWRITE) { 382 lchange(WFEDIT); 383 while (curwp->w_doto < llength(curwp->w_dotp) && n--) 384 lputc(curwp->w_dotp, curwp->w_doto++, c); 385 if (n <= 0) 386 return (TRUE); 387 } 388 return (linsert(n, c)); 389 } 390 391 /* 392 * selfinsert() can't be called directly from a startup file or by 393 * 'eval-current-buffer' since it is by design, meant to be called interactively 394 * as characters are typed in a buffer. ask_selfinsert() allows selfinsert() to 395 * be used by excline(). Having ask_selfinsert() helps with regression testing. 396 * No manual page entry since use case is a bit obscure. See 'insert' command. 397 */ 398 int 399 ask_selfinsert(int f, int n) 400 { 401 char *c, cbuf[2]; 402 403 if ((c = eread("Insert a character: ", cbuf, sizeof(cbuf), 404 EFNEW)) == NULL || (c[0] == '\0')) 405 return (ABORT); 406 407 key.k_chars[0] = *c; 408 key.k_chars[1] = '\0'; 409 key.k_count = 1; 410 411 return (selfinsert(FFRAND, 1)); 412 } 413 414 /* 415 * This could be implemented as a keymap with everything defined as self-insert. 416 */ 417 int 418 quote(int f, int n) 419 { 420 int c; 421 422 key.k_count = 1; 423 if ((key.k_chars[0] = getkey(TRUE)) >= '0' && key.k_chars[0] <= '7') { 424 key.k_chars[0] -= '0'; 425 if ((c = getkey(TRUE)) >= '0' && c <= '7') { 426 key.k_chars[0] <<= 3; 427 key.k_chars[0] += c - '0'; 428 if ((c = getkey(TRUE)) >= '0' && c <= '7') { 429 key.k_chars[0] <<= 3; 430 key.k_chars[0] += c - '0'; 431 } else 432 ungetkey(c); 433 } else 434 ungetkey(c); 435 } 436 return (selfinsert(f, n)); 437 } 438 439 /* 440 * Wrapper function to count invocation repeats. 441 * We ignore any function whose sole purpose is to get us 442 * to the intended function. 443 */ 444 static int 445 mgwrap(PF funct, int f, int n) 446 { 447 static PF ofp; 448 449 if (funct != rescan && 450 funct != negative_argument && 451 funct != digit_argument && 452 funct != universal_argument) { 453 if (funct == ofp) 454 rptcount++; 455 else 456 rptcount = 0; 457 ofp = funct; 458 } 459 460 return ((*funct)(f, n)); 461 } 462