/* $OpenBSD: kbd.c,v 1.33 2019/07/03 18:11:07 lum Exp $ */ /* This file is in the public domain. */ /* * Terminal independent keyboard handling. */ #include #include #include #include "def.h" #include "kbd.h" #include "key.h" #include "macro.h" #ifdef MGLOG #include "log.h" #endif #define METABIT 0x80 #define PROMPTL 80 char prompt[PROMPTL] = "", *promptp = prompt; static int mgwrap(PF, int, int); static int use_metakey = TRUE; static int pushed = FALSE; static int pushedc; struct map_element *ele; struct key key; /* * Toggle the value of use_metakey */ int do_meta(int f, int n) { if (f & FFARG) use_metakey = n > 0; else use_metakey = !use_metakey; ewprintf("Meta keys %sabled", use_metakey ? "en" : "dis"); return (TRUE); } static int bs_map = 0; /* * Toggle backspace mapping */ int bsmap(int f, int n) { if (f & FFARG) bs_map = n > 0; else bs_map = !bs_map; ewprintf("Backspace mapping %sabled", bs_map ? "en" : "dis"); return (TRUE); } void ungetkey(int c) { if (use_metakey && pushed && c == CCHR('[')) pushedc |= METABIT; else pushedc = c; pushed = TRUE; } int getkey(int flag) { int c; if (flag && !pushed) { if (prompt[0] != '\0' && ttwait(2000)) { /* avoid problems with % */ ewprintf("%s", prompt); /* put the cursor back */ update(CMODE); epresf = KCLEAR; } if (promptp > prompt) *(promptp - 1) = ' '; } if (pushed) { c = pushedc; pushed = FALSE; } else c = ttgetc(); if (bs_map) { if (c == CCHR('H')) c = CCHR('?'); else if (c == CCHR('?')) c = CCHR('H'); } if (use_metakey && (c & METABIT)) { pushedc = c & ~METABIT; pushed = TRUE; c = CCHR('['); } if (flag && promptp < &prompt[PROMPTL - 5]) { promptp = getkeyname(promptp, sizeof(prompt) - (promptp - prompt) - 1, c); *promptp++ = '-'; *promptp = '\0'; } return (c); } /* * doscan scans a keymap for a keyboard character and returns a pointer * to the function associated with that character. Sets ele to the * keymap element the keyboard was found in as a side effect. */ PF doscan(KEYMAP *map, int c, KEYMAP **newmap) { struct map_element *elec = &map->map_element[0]; struct map_element *last = &map->map_element[map->map_num]; PF ret; while (elec < last && c > elec->k_num) elec++; /* used by prefix and binding code */ ele = elec; if (elec >= last || c < elec->k_base) ret = map->map_default; else ret = elec->k_funcp[c - elec->k_base]; if (ret == NULL && newmap != NULL) *newmap = elec->k_prefmap; return (ret); } int doin(void) { KEYMAP *curmap; PF funct; *(promptp = prompt) = '\0'; curmap = curbp->b_modes[curbp->b_nmodes]->p_map; key.k_count = 0; while ((funct = doscan(curmap, (key.k_chars[key.k_count++] = getkey(TRUE)), &curmap)) == NULL) /* nothing */; #ifdef MGLOG if (!mglog(funct, curmap)) ewprintf("Problem with logging"); #endif if (macrodef && macrocount < MAXMACRO) macro[macrocount++].m_funct = funct; return (mgwrap(funct, 0, 1)); } int rescan(int f, int n) { int c; KEYMAP *curmap; int i; PF fp = NULL; int md = curbp->b_nmodes; for (;;) { if (ISUPPER(key.k_chars[key.k_count - 1])) { c = TOLOWER(key.k_chars[key.k_count - 1]); curmap = curbp->b_modes[md]->p_map; for (i = 0; i < key.k_count - 1; i++) { if ((fp = doscan(curmap, (key.k_chars[i]), &curmap)) != NULL) break; } if (fp == NULL) { if ((fp = doscan(curmap, c, NULL)) == NULL) while ((fp = doscan(curmap, key.k_chars[key.k_count++] = getkey(TRUE), &curmap)) == NULL) /* nothing */; if (fp != rescan) { if (macrodef && macrocount <= MAXMACRO) macro[macrocount - 1].m_funct = fp; return (mgwrap(fp, f, n)); } } } /* try previous mode */ if (--md < 0) return (ABORT); curmap = curbp->b_modes[md]->p_map; for (i = 0; i < key.k_count; i++) { if ((fp = doscan(curmap, (key.k_chars[i]), &curmap)) != NULL) break; } if (fp == NULL) { while ((fp = doscan(curmap, key.k_chars[i++] = getkey(TRUE), &curmap)) == NULL) /* nothing */; key.k_count = i; } if (fp != rescan && i >= key.k_count - 1) { if (macrodef && macrocount <= MAXMACRO) macro[macrocount - 1].m_funct = fp; return (mgwrap(fp, f, n)); } } } int universal_argument(int f, int n) { KEYMAP *curmap; PF funct; int c, nn = 4; if (f & FFUNIV) nn *= n; for (;;) { key.k_chars[0] = c = getkey(TRUE); key.k_count = 1; if (c == '-') return (negative_argument(f, nn)); if (c >= '0' && c <= '9') return (digit_argument(f, nn)); curmap = curbp->b_modes[curbp->b_nmodes]->p_map; while ((funct = doscan(curmap, c, &curmap)) == NULL) { key.k_chars[key.k_count++] = c = getkey(TRUE); } if (funct != universal_argument) { if (macrodef && macrocount < MAXMACRO - 1) { if (f & FFARG) macrocount--; macro[macrocount++].m_count = nn; macro[macrocount++].m_funct = funct; } return (mgwrap(funct, FFUNIV, nn)); } nn <<= 2; } } /* ARGSUSED */ int digit_argument(int f, int n) { KEYMAP *curmap; PF funct; int nn, c; nn = key.k_chars[key.k_count - 1] - '0'; for (;;) { c = getkey(TRUE); if (c < '0' || c > '9') break; nn *= 10; nn += c - '0'; } key.k_chars[0] = c; key.k_count = 1; curmap = curbp->b_modes[curbp->b_nmodes]->p_map; while ((funct = doscan(curmap, c, &curmap)) == NULL) { key.k_chars[key.k_count++] = c = getkey(TRUE); } if (macrodef && macrocount < MAXMACRO - 1) { if (f & FFARG) macrocount--; else macro[macrocount - 1].m_funct = universal_argument; macro[macrocount++].m_count = nn; macro[macrocount++].m_funct = funct; } return (mgwrap(funct, FFOTHARG, nn)); } int negative_argument(int f, int n) { KEYMAP *curmap; PF funct; int c; int nn = 0; for (;;) { c = getkey(TRUE); if (c < '0' || c > '9') break; nn *= 10; nn += c - '0'; } if (nn) nn = -nn; else nn = -n; key.k_chars[0] = c; key.k_count = 1; curmap = curbp->b_modes[curbp->b_nmodes]->p_map; while ((funct = doscan(curmap, c, &curmap)) == NULL) { key.k_chars[key.k_count++] = c = getkey(TRUE); } if (macrodef && macrocount < MAXMACRO - 1) { if (f & FFARG) macrocount--; else macro[macrocount - 1].m_funct = universal_argument; macro[macrocount++].m_count = nn; macro[macrocount++].m_funct = funct; } return (mgwrap(funct, FFNEGARG, nn)); } /* * Insert a character. While defining a macro, create a "LINE" containing * all inserted characters. */ int selfinsert(int f, int n) { struct line *lp; int c; int count; if (n < 0) return (FALSE); if (n == 0) return (TRUE); c = key.k_chars[key.k_count - 1]; if (macrodef && macrocount < MAXMACRO) { if (f & FFARG) macrocount -= 2; /* last command was insert -- tack on the end */ if (lastflag & CFINS) { macrocount--; /* Ensure the line can handle the new characters */ if (maclcur->l_size < maclcur->l_used + n) { if (lrealloc(maclcur, maclcur->l_used + n) == FALSE) return (FALSE); } maclcur->l_used += n; /* Copy in the new data */ for (count = maclcur->l_used - n; count < maclcur->l_used; count++) maclcur->l_text[count] = c; } else { macro[macrocount - 1].m_funct = insert; if ((lp = lalloc(n)) == NULL) return (FALSE); lp->l_bp = maclcur; lp->l_fp = maclcur->l_fp; maclcur->l_fp = lp; maclcur = lp; for (count = 0; count < n; count++) lp->l_text[count] = c; } thisflag |= CFINS; } if (c == '\n') { do { count = lnewline(); } while (--n && count == TRUE); return (count); } /* overwrite mode */ if (curbp->b_flag & BFOVERWRITE) { lchange(WFEDIT); while (curwp->w_doto < llength(curwp->w_dotp) && n--) lputc(curwp->w_dotp, curwp->w_doto++, c); if (n <= 0) return (TRUE); } return (linsert(n, c)); } /* * selfinsert() can't be called directly from a startup file or by * 'eval-current-buffer' since it is by design, meant to be called interactively * as characters are typed in a buffer. ask_selfinsert() allows selfinsert() to * be used by excline(). Having ask_selfinsert() helps with regression testing. * No manual page entry since use case is a bit obscure. See 'insert' command. */ int ask_selfinsert(int f, int n) { char *c, cbuf[2]; if ((c = eread("Insert a character: ", cbuf, sizeof(cbuf), EFNEW)) == NULL || (c[0] == '\0')) return (ABORT); key.k_chars[0] = *c; key.k_chars[1] = '\0'; key.k_count = 1; return (selfinsert(FFRAND, 1)); } /* * This could be implemented as a keymap with everything defined as self-insert. */ int quote(int f, int n) { int c; key.k_count = 1; if ((key.k_chars[0] = getkey(TRUE)) >= '0' && key.k_chars[0] <= '7') { key.k_chars[0] -= '0'; if ((c = getkey(TRUE)) >= '0' && c <= '7') { key.k_chars[0] <<= 3; key.k_chars[0] += c - '0'; if ((c = getkey(TRUE)) >= '0' && c <= '7') { key.k_chars[0] <<= 3; key.k_chars[0] += c - '0'; } else ungetkey(c); } else ungetkey(c); } return (selfinsert(f, n)); } /* * Wraper function to count invocation repeats. * We ignore any function whose sole purpose is to get us * to the intended function. */ static int mgwrap(PF funct, int f, int n) { static PF ofp; if (funct != rescan && funct != negative_argument && funct != digit_argument && funct != universal_argument) { if (funct == ofp) rptcount++; else rptcount = 0; ofp = funct; } return ((*funct)(f, n)); }