1 /* $OpenBSD: extend.c,v 1.80 2023/04/17 10:11:30 op Exp $ */ 2 /* This file is in the public domain. */ 3 4 /* 5 * Extended (M-x) commands, rebinding, and startup file processing. 6 */ 7 8 #include <sys/queue.h> 9 #include <sys/types.h> 10 #include <regex.h> 11 #include <ctype.h> 12 #include <limits.h> 13 #include <signal.h> 14 #include <stdio.h> 15 #include <stdlib.h> 16 #include <string.h> 17 18 #include "chrdef.h" 19 #include "def.h" 20 #include "funmap.h" 21 #include "kbd.h" 22 #include "key.h" 23 #include "macro.h" 24 25 static int remap(KEYMAP *, int, PF, KEYMAP *); 26 static KEYMAP *reallocmap(KEYMAP *); 27 static void fixmap(KEYMAP *, KEYMAP *, KEYMAP *); 28 static int dobind(KEYMAP *, const char *, int); 29 static char *parsetoken(char *); 30 static int bindkey(KEYMAP **, const char *, KCHAR *, int); 31 32 /* 33 * Insert a string, mainly for use from macros (created by selfinsert). 34 */ 35 int 36 insert(int f, int n) 37 { 38 char buf[BUFSIZE], *bufp, *cp; 39 int count, c; 40 41 if (inmacro) { 42 while (--n >= 0) { 43 for (count = 0; count < maclcur->l_used; count++) { 44 if ((((c = maclcur->l_text[count]) == 45 *curbp->b_nlchr) 46 ? lnewline() : linsert(1, c)) != TRUE) 47 return (FALSE); 48 } 49 } 50 maclcur = maclcur->l_fp; 51 return (TRUE); 52 } 53 if (n == 1) 54 /* CFINS means selfinsert can tack on the end */ 55 thisflag |= CFINS; 56 57 if ((bufp = eread("Insert: ", buf, sizeof(buf), EFNEW)) == NULL) 58 return (ABORT); 59 else if (bufp[0] == '\0') 60 return (FALSE); 61 while (--n >= 0) { 62 cp = buf; 63 while (*cp) { 64 if (((*cp == *curbp->b_nlchr) ? 65 lnewline() : linsert(1, *cp)) 66 != TRUE) 67 return (FALSE); 68 cp++; 69 } 70 } 71 return (TRUE); 72 } 73 74 /* 75 * Bind a key to a function. Cases range from the trivial (replacing an 76 * existing binding) to the extremely complex (creating a new prefix in a 77 * map_element that already has one, so the map_element must be split, 78 * but the keymap doesn't have enough room for another map_element, so 79 * the keymap is reallocated). No attempt is made to reclaim space no 80 * longer used, if this is a problem flags must be added to indicate 81 * malloced versus static storage in both keymaps and map_elements. 82 * Structure assignments would come in real handy, but K&R based compilers 83 * don't have them. Care is taken so running out of memory will leave 84 * the keymap in a usable state. 85 * Parameters are: 86 * curmap: pointer to the map being changed 87 * c: character being changed 88 * funct: function being changed to 89 * pref_map: if funct==NULL, map to bind to or NULL for new 90 */ 91 static int 92 remap(KEYMAP *curmap, int c, PF funct, KEYMAP *pref_map) 93 { 94 int i, n1, n2, nold; 95 KEYMAP *mp, *newmap; 96 PF *pfp; 97 struct map_element *mep; 98 99 if (ele >= &curmap->map_element[curmap->map_num] || c < ele->k_base) { 100 if (ele > &curmap->map_element[0] && (funct != NULL || 101 (ele - 1)->k_prefmap == NULL)) 102 n1 = c - (ele - 1)->k_num; 103 else 104 n1 = HUGE; 105 if (ele < &curmap->map_element[curmap->map_num] && 106 (funct != NULL || ele->k_prefmap == NULL)) 107 n2 = ele->k_base - c; 108 else 109 n2 = HUGE; 110 if (n1 <= MAPELEDEF && n1 <= n2) { 111 ele--; 112 if ((pfp = calloc(c - ele->k_base + 1, 113 sizeof(PF))) == NULL) 114 return (dobeep_msg("Out of memory")); 115 116 nold = ele->k_num - ele->k_base + 1; 117 for (i = 0; i < nold; i++) 118 pfp[i] = ele->k_funcp[i]; 119 while (--n1) 120 pfp[i++] = curmap->map_default; 121 pfp[i] = funct; 122 ele->k_num = c; 123 ele->k_funcp = pfp; 124 } else if (n2 <= MAPELEDEF) { 125 if ((pfp = calloc(ele->k_num - c + 1, 126 sizeof(PF))) == NULL) 127 return (dobeep_msg("Out of memory")); 128 129 nold = ele->k_num - ele->k_base + 1; 130 for (i = 0; i < nold; i++) 131 pfp[i + n2] = ele->k_funcp[i]; 132 while (--n2) 133 pfp[n2] = curmap->map_default; 134 pfp[0] = funct; 135 ele->k_base = c; 136 ele->k_funcp = pfp; 137 } else { 138 if (curmap->map_num >= curmap->map_max) { 139 if ((newmap = reallocmap(curmap)) == NULL) 140 return (FALSE); 141 curmap = newmap; 142 } 143 if ((pfp = malloc(sizeof(PF))) == NULL) 144 return (dobeep_msg("Out of memory")); 145 146 pfp[0] = funct; 147 for (mep = &curmap->map_element[curmap->map_num]; 148 mep > ele; mep--) { 149 mep->k_base = (mep - 1)->k_base; 150 mep->k_num = (mep - 1)->k_num; 151 mep->k_funcp = (mep - 1)->k_funcp; 152 mep->k_prefmap = (mep - 1)->k_prefmap; 153 } 154 ele->k_base = c; 155 ele->k_num = c; 156 ele->k_funcp = pfp; 157 ele->k_prefmap = NULL; 158 curmap->map_num++; 159 } 160 if (funct == NULL) { 161 if (pref_map != NULL) 162 ele->k_prefmap = pref_map; 163 else { 164 if ((mp = malloc(sizeof(KEYMAP) + 165 (MAPINIT - 1) * sizeof(struct map_element))) == NULL) { 166 (void)dobeep_msg("Out of memory"); 167 ele->k_funcp[c - ele->k_base] = 168 curmap->map_default; 169 return (FALSE); 170 } 171 mp->map_num = 0; 172 mp->map_max = MAPINIT; 173 mp->map_default = rescan; 174 ele->k_prefmap = mp; 175 } 176 } 177 } else { 178 n1 = c - ele->k_base; 179 if (ele->k_funcp[n1] == funct && (funct != NULL || 180 pref_map == NULL || pref_map == ele->k_prefmap)) 181 /* no change */ 182 return (TRUE); 183 if (funct != NULL || ele->k_prefmap == NULL) { 184 if (ele->k_funcp[n1] == NULL) 185 ele->k_prefmap = NULL; 186 /* easy case */ 187 ele->k_funcp[n1] = funct; 188 if (funct == NULL) { 189 if (pref_map != NULL) 190 ele->k_prefmap = pref_map; 191 else { 192 if ((mp = malloc(sizeof(KEYMAP) + 193 (MAPINIT - 1) * 194 sizeof(struct map_element))) == NULL) { 195 (void)dobeep_msg("Out of memory"); 196 ele->k_funcp[c - ele->k_base] = 197 curmap->map_default; 198 return (FALSE); 199 } 200 mp->map_num = 0; 201 mp->map_max = MAPINIT; 202 mp->map_default = rescan; 203 ele->k_prefmap = mp; 204 } 205 } 206 } else { 207 /* 208 * This case is the splits. 209 * Determine which side of the break c goes on 210 * 0 = after break; 1 = before break 211 */ 212 n2 = 1; 213 for (i = 0; n2 && i < n1; i++) 214 n2 &= ele->k_funcp[i] != NULL; 215 if (curmap->map_num >= curmap->map_max) { 216 if ((newmap = reallocmap(curmap)) == NULL) 217 return (FALSE); 218 curmap = newmap; 219 } 220 if ((pfp = calloc(ele->k_num - c + !n2, 221 sizeof(PF))) == NULL) 222 return (dobeep_msg("Out of memory")); 223 224 ele->k_funcp[n1] = NULL; 225 for (i = n1 + n2; i <= ele->k_num - ele->k_base; i++) 226 pfp[i - n1 - n2] = ele->k_funcp[i]; 227 for (mep = &curmap->map_element[curmap->map_num]; 228 mep > ele; mep--) { 229 mep->k_base = (mep - 1)->k_base; 230 mep->k_num = (mep - 1)->k_num; 231 mep->k_funcp = (mep - 1)->k_funcp; 232 mep->k_prefmap = (mep - 1)->k_prefmap; 233 } 234 ele->k_num = c - !n2; 235 (ele + 1)->k_base = c + n2; 236 (ele + 1)->k_funcp = pfp; 237 ele += !n2; 238 ele->k_prefmap = NULL; 239 curmap->map_num++; 240 if (pref_map == NULL) { 241 if ((mp = malloc(sizeof(KEYMAP) + (MAPINIT - 1) 242 * sizeof(struct map_element))) == NULL) { 243 (void)dobeep_msg("Out of memory"); 244 ele->k_funcp[c - ele->k_base] = 245 curmap->map_default; 246 return (FALSE); 247 } 248 mp->map_num = 0; 249 mp->map_max = MAPINIT; 250 mp->map_default = rescan; 251 ele->k_prefmap = mp; 252 } else 253 ele->k_prefmap = pref_map; 254 } 255 } 256 return (TRUE); 257 } 258 259 /* 260 * Reallocate a keymap. Returns NULL (without trashing the current map) 261 * on failure. 262 */ 263 static KEYMAP * 264 reallocmap(KEYMAP *curmap) 265 { 266 struct maps_s *mps; 267 KEYMAP *mp; 268 int i; 269 270 if (curmap->map_max > SHRT_MAX - MAPGROW) { 271 (void)dobeep_msg("keymap too large"); 272 return (NULL); 273 } 274 if ((mp = malloc(sizeof(KEYMAP) + (curmap->map_max + (MAPGROW - 1)) * 275 sizeof(struct map_element))) == NULL) { 276 (void)dobeep_msg("Out of memory"); 277 return (NULL); 278 } 279 mp->map_num = curmap->map_num; 280 mp->map_max = curmap->map_max + MAPGROW; 281 mp->map_default = curmap->map_default; 282 for (i = curmap->map_num; i--;) { 283 mp->map_element[i].k_base = curmap->map_element[i].k_base; 284 mp->map_element[i].k_num = curmap->map_element[i].k_num; 285 mp->map_element[i].k_funcp = curmap->map_element[i].k_funcp; 286 mp->map_element[i].k_prefmap = curmap->map_element[i].k_prefmap; 287 } 288 for (mps = maps; mps != NULL; mps = mps->p_next) { 289 if (mps->p_map == curmap) 290 mps->p_map = mp; 291 else 292 fixmap(curmap, mp, mps->p_map); 293 } 294 ele = &mp->map_element[ele - &curmap->map_element[0]]; 295 return (mp); 296 } 297 298 /* 299 * Fix references to a reallocated keymap (recursive). 300 */ 301 static void 302 fixmap(KEYMAP *curmap, KEYMAP *mp, KEYMAP *mt) 303 { 304 int i; 305 306 for (i = mt->map_num; i--;) { 307 if (mt->map_element[i].k_prefmap != NULL) { 308 if (mt->map_element[i].k_prefmap == curmap) 309 mt->map_element[i].k_prefmap = mp; 310 else 311 fixmap(curmap, mp, mt->map_element[i].k_prefmap); 312 } 313 } 314 } 315 316 /* 317 * Do the input for local-set-key, global-set-key and define-key 318 * then call remap to do the work. 319 */ 320 static int 321 dobind(KEYMAP *curmap, const char *p, int unbind) 322 { 323 KEYMAP *pref_map = NULL; 324 PF funct; 325 char bprompt[80], *bufp, *pep; 326 int c, s, n; 327 328 if (macrodef) { 329 /* 330 * Keystrokes aren't collected. Not hard, but pretty useless. 331 * Would not work for function keys in any case. 332 */ 333 return (dobeep_msg("Can't rebind key in macro")); 334 } 335 if (inmacro) { 336 for (s = 0; s < maclcur->l_used - 1; s++) { 337 if (doscan(curmap, c = CHARMASK(maclcur->l_text[s]), &curmap) 338 != NULL) { 339 if (remap(curmap, c, NULL, NULL) 340 != TRUE) 341 return (FALSE); 342 } 343 } 344 (void)doscan(curmap, c = maclcur->l_text[s], NULL); 345 maclcur = maclcur->l_fp; 346 } else { 347 n = strlcpy(bprompt, p, sizeof(bprompt)); 348 if (n >= sizeof(bprompt)) 349 n = sizeof(bprompt) - 1; 350 pep = bprompt + n; 351 for (;;) { 352 ewprintf("%s", bprompt); 353 pep[-1] = ' '; 354 pep = getkeyname(pep, sizeof(bprompt) - 355 (pep - bprompt), c = getkey(FALSE)); 356 if (doscan(curmap, c, &curmap) != NULL) 357 break; 358 *pep++ = '-'; 359 *pep = '\0'; 360 } 361 } 362 if (unbind) 363 funct = rescan; 364 else { 365 if ((bufp = eread("%s to command: ", bprompt, sizeof(bprompt), 366 EFFUNC | EFNEW, bprompt)) == NULL) 367 return (ABORT); 368 else if (bufp[0] == '\0') 369 return (FALSE); 370 if (((funct = name_function(bprompt)) == NULL) ? 371 (pref_map = name_map(bprompt)) == NULL : funct == NULL) 372 return (dobeep_msg("[No match]")); 373 374 } 375 return (remap(curmap, c, funct, pref_map)); 376 } 377 378 /* 379 * bindkey: bind key sequence to a function in the specified map. Used by 380 * excline so it can bind function keys. To close to release to change 381 * calling sequence, should just pass KEYMAP *curmap rather than 382 * KEYMAP **mapp. 383 */ 384 static int 385 bindkey(KEYMAP **mapp, const char *fname, KCHAR *keys, int kcount) 386 { 387 KEYMAP *curmap = *mapp; 388 KEYMAP *pref_map = NULL; 389 PF funct; 390 int c; 391 392 if (fname == NULL) 393 funct = rescan; 394 else if (((funct = name_function(fname)) == NULL) ? 395 (pref_map = name_map(fname)) == NULL : funct == NULL) { 396 dobeep(); 397 ewprintf("[No match: %s]", fname); 398 return (FALSE); 399 } 400 while (--kcount) { 401 if (doscan(curmap, c = *keys++, &curmap) != NULL) { 402 if (remap(curmap, c, NULL, NULL) != TRUE) 403 return (FALSE); 404 /* 405 * XXX - Bizzarreness. remap creates an empty KEYMAP 406 * that the last key is supposed to point to. 407 */ 408 curmap = ele->k_prefmap; 409 } 410 } 411 (void)doscan(curmap, c = *keys, NULL); 412 return (remap(curmap, c, funct, pref_map)); 413 } 414 415 /* 416 * Wrapper for bindkey() that converts escapes. 417 */ 418 int 419 dobindkey(KEYMAP *map, const char *func, const char *str) 420 { 421 int i; 422 423 for (i = 0; *str && i < MAXKEY; i++) { 424 /* XXX - convert numbers w/ strol()? */ 425 if (*str == '^' && *(str + 1) != '\0') { 426 key.k_chars[i] = CCHR(toupper((unsigned char)*++str)); 427 } else if (*str == '\\' && *(str + 1) != '\0') { 428 switch (*++str) { 429 case '^': 430 key.k_chars[i] = '^'; 431 break; 432 case 't': 433 case 'T': 434 key.k_chars[i] = '\t'; 435 break; 436 case 'n': 437 case 'N': 438 key.k_chars[i] = *curbp->b_nlchr; 439 break; 440 case 'r': 441 case 'R': 442 key.k_chars[i] = '\r'; 443 break; 444 case 'e': 445 case 'E': 446 key.k_chars[i] = CCHR('['); 447 break; 448 case '\\': 449 key.k_chars[i] = '\\'; 450 break; 451 } 452 } else 453 key.k_chars[i] = *str; 454 str++; 455 } 456 key.k_count = i; 457 return (bindkey(&map, func, key.k_chars, key.k_count)); 458 } 459 460 /* 461 * This function modifies the fundamental keyboard map. 462 */ 463 int 464 bindtokey(int f, int n) 465 { 466 return (dobind(fundamental_map, "Global set key: ", FALSE)); 467 } 468 469 /* 470 * This function modifies the current mode's keyboard map. 471 */ 472 int 473 localbind(int f, int n) 474 { 475 return (dobind(curbp->b_modes[curbp->b_nmodes]->p_map, 476 "Local set key: ", FALSE)); 477 } 478 479 /* 480 * This function redefines a key in any keymap. 481 */ 482 int 483 redefine_key(int f, int n) 484 { 485 static char buf[48]; 486 char tmp[32], *bufp; 487 KEYMAP *mp; 488 489 (void)strlcpy(buf, "Define key map: ", sizeof(buf)); 490 if ((bufp = eread("%s", tmp, sizeof(tmp), EFNEW, buf)) == NULL) 491 return (ABORT); 492 else if (bufp[0] == '\0') 493 return (FALSE); 494 (void)strlcat(buf, tmp, sizeof(buf)); 495 if ((mp = name_map(tmp)) == NULL) 496 return (dobeep_msgs("Unknown map", tmp)); 497 498 if (strlcat(buf, "key: ", sizeof(buf)) >= sizeof(buf)) 499 return (FALSE); 500 501 return (dobind(mp, buf, FALSE)); 502 } 503 504 int 505 unbindtokey(int f, int n) 506 { 507 return (dobind(fundamental_map, "Global unset key: ", TRUE)); 508 } 509 510 int 511 localunbind(int f, int n) 512 { 513 return (dobind(curbp->b_modes[curbp->b_nmodes]->p_map, 514 "Local unset key: ", TRUE)); 515 } 516 517 /* 518 * Extended command. Call the message line routine to read in the command 519 * name and apply autocompletion to it. When it comes back, look the name 520 * up in the symbol table and run the command if it is found. Print an 521 * error if there is anything wrong. 522 */ 523 int 524 extend(int f, int n) 525 { 526 PF funct; 527 char xname[NXNAME], *bufp; 528 529 if (!(f & FFARG)) 530 bufp = eread("M-x ", xname, NXNAME, EFNEW | EFFUNC); 531 else 532 bufp = eread("%d M-x ", xname, NXNAME, EFNEW | EFFUNC, n); 533 if (bufp == NULL) 534 return (ABORT); 535 else if (bufp[0] == '\0') 536 return (FALSE); 537 if ((funct = name_function(bufp)) != NULL) { 538 if (macrodef) { 539 struct line *lp = maclcur; 540 macro[macrocount - 1].m_funct = funct; 541 maclcur = lp->l_bp; 542 maclcur->l_fp = lp->l_fp; 543 free(lp); 544 } 545 return ((*funct)(f, n)); 546 } 547 return (dobeep_msg("[No match]")); 548 } 549 550 /* 551 * Define the commands needed to do startup-file processing. 552 * This code is mostly a kludge just so we can get startup-file processing. 553 * 554 * If you're serious about having this code, you should rewrite it. 555 * To wit: 556 * It has lots of funny things in it to make the startup-file look 557 * like a GNU startup file; mostly dealing with parens and semicolons. 558 * This should all vanish. 559 * 560 * We define eval-expression because it's easy. It can make 561 * *-set-key or define-key set an arbitrary key sequence, so it isn't 562 * useless. 563 */ 564 565 /* 566 * evalexpr - get one line from the user, and run it. 567 * Use strlen for length of line, assume user is not typing in a '\0' in the 568 * modeline. llen only used for foundparen() so old-school will be ok. 569 */ 570 int 571 evalexpr(int f, int n) 572 { 573 char exbuf[BUFSIZE], *bufp; 574 int llen; 575 576 if ((bufp = eread("Eval: ", exbuf, sizeof(exbuf), 577 EFNEW | EFCR)) == NULL) 578 return (ABORT); 579 else if (bufp[0] == '\0') 580 return (FALSE); 581 llen = strlen(bufp); 582 583 return (excline(exbuf, llen, 1)); 584 } 585 586 /* 587 * evalbuffer - evaluate the current buffer as line commands. Useful for 588 * testing startup files. 589 */ 590 int 591 evalbuffer(int f, int n) 592 { 593 struct line *lp; 594 struct buffer *bp = curbp; 595 int s, llen, lnum = 0; 596 static char excbuf[BUFSIZE]; 597 598 for (lp = bfirstlp(bp); lp != bp->b_headp; lp = lforw(lp)) { 599 lnum++; 600 llen = llength(lp); 601 if (llen >= BUFSIZE) 602 return (FALSE); 603 (void)strncpy(excbuf, ltext(lp), llen); 604 605 /* make sure the line is terminated */ 606 excbuf[llen] = '\0'; 607 if ((s = excline(excbuf, llen, lnum)) != TRUE) { 608 cleanup(); 609 return (s); 610 } 611 } 612 cleanup(); 613 return (TRUE); 614 } 615 616 /* 617 * evalfile - go get a file and evaluate it as line commands. You can 618 * go get your own startup file if need be. 619 */ 620 int 621 evalfile(int f, int n) 622 { 623 FILE *ffp; 624 char fname[NFILEN], *bufp; 625 int ret; 626 627 if ((bufp = eread("Load file: ", fname, NFILEN, 628 EFNEW | EFCR)) == NULL) 629 return (ABORT); 630 if (bufp[0] == '\0') 631 return (FALSE); 632 if ((bufp = adjustname(fname, TRUE)) == NULL) 633 return (FALSE); 634 ret = ffropen(&ffp, bufp, NULL); 635 if (ret == FIODIR) 636 (void)ffclose(ffp, NULL); 637 if (ret != FIOSUC) 638 return (FALSE); 639 ret = load(ffp, bufp); 640 (void)ffclose(ffp, NULL); 641 return (ret); 642 } 643 644 /* 645 * load - go load the file name we got passed. 646 */ 647 int 648 load(FILE *ffp, const char *fname) 649 { 650 int s = TRUE, line; 651 int nbytes = 0; 652 char excbuf[BUFSIZE]; 653 654 line = 0; 655 while ((s = ffgetline(ffp, excbuf, sizeof(excbuf) - 1, &nbytes)) 656 == FIOSUC) { 657 line++; 658 excbuf[nbytes] = '\0'; 659 if (excline(excbuf, nbytes, line) != TRUE) { 660 s = FIOERR; 661 dobeep(); 662 ewprintf("Error loading file %s at line %d", fname, line); 663 break; 664 } 665 } 666 excbuf[nbytes] = '\0'; 667 if (s != FIOEOF || (nbytes && excline(excbuf, nbytes, ++line) != TRUE)) 668 return (FALSE); 669 return (TRUE); 670 } 671 672 /* 673 * excline - run a line from a load file or eval-expression. 674 */ 675 int 676 excline(char *line, int llen, int lnum) 677 { 678 PF fp; 679 struct line *lp, *np; 680 int status, c, f, n; 681 char *funcp, *tmp; 682 char *argp = NULL; 683 long nl; 684 int bind; 685 KEYMAP *curmap; 686 #define BINDARG 0 /* this arg is key to bind (local/global set key) */ 687 #define BINDNO 1 /* not binding or non-quoted BINDARG */ 688 #define BINDNEXT 2 /* next arg " (define-key) */ 689 #define BINDDO 3 /* already found key to bind */ 690 #define BINDEXT 1 /* space for trailing \0 */ 691 692 lp = NULL; 693 694 if (macrodef || inmacro) 695 return (dobeep_msg("Not now!")); 696 697 f = 0; 698 n = 1; 699 funcp = skipwhite(line); 700 if (*funcp == '\0') 701 return (TRUE); /* No error on blank lines */ 702 if (*funcp == '(') 703 return (foundparen(funcp, llen, lnum)); 704 line = parsetoken(funcp); 705 if (*line != '\0') { 706 *line++ = '\0'; 707 line = skipwhite(line); 708 if (ISDIGIT(*line) || *line == '-') { 709 argp = line; 710 line = parsetoken(line); 711 } 712 } 713 if (argp != NULL) { 714 f = FFARG; 715 nl = strtol(argp, &tmp, 10); 716 if (*tmp != '\0') 717 return (FALSE); 718 if (nl >= INT_MAX || nl <= INT_MIN) 719 return (FALSE); 720 n = (int)nl; 721 } 722 if ((fp = name_function(funcp)) == NULL) 723 return (dobeep_msgs("Unknown function:", funcp)); 724 725 if (fp == bindtokey || fp == unbindtokey) { 726 bind = BINDARG; 727 curmap = fundamental_map; 728 } else if (fp == localbind || fp == localunbind) { 729 bind = BINDARG; 730 curmap = curbp->b_modes[curbp->b_nmodes]->p_map; 731 } else if (fp == redefine_key) 732 bind = BINDNEXT; 733 else 734 bind = BINDNO; 735 /* Pack away all the args now... */ 736 if ((np = lalloc(0)) == FALSE) 737 return (FALSE); 738 np->l_fp = np->l_bp = maclcur = np; 739 while (*line != '\0') { 740 argp = skipwhite(line); 741 if (*argp == '\0') 742 break; 743 line = parsetoken(argp); 744 if (*argp != '"') { 745 if (*argp == '\'') 746 ++argp; 747 if ((lp = lalloc((int) (line - argp) + BINDEXT)) == 748 NULL) { 749 status = FALSE; 750 goto cleanup; 751 } 752 bcopy(argp, ltext(lp), (int)(line - argp)); 753 /* don't count BINDEXT */ 754 lp->l_used--; 755 if (bind == BINDARG) 756 bind = BINDNO; 757 } else { 758 /* quoted strings are special */ 759 ++argp; 760 if (bind != BINDARG) { 761 lp = lalloc((int)(line - argp) + BINDEXT); 762 if (lp == NULL) { 763 status = FALSE; 764 goto cleanup; 765 } 766 lp->l_used = 0; 767 } else 768 key.k_count = 0; 769 while (*argp != '"' && *argp != '\0') { 770 if (*argp != '\\') 771 c = *argp++; 772 else { 773 switch (*++argp) { 774 case 't': 775 case 'T': 776 c = CCHR('I'); 777 break; 778 case 'n': 779 case 'N': 780 c = CCHR('J'); 781 break; 782 case 'r': 783 case 'R': 784 c = CCHR('M'); 785 break; 786 case 'e': 787 case 'E': 788 c = CCHR('['); 789 break; 790 case '^': 791 /* 792 * split into two statements 793 * due to bug in OSK cpp 794 */ 795 c = CHARMASK(*++argp); 796 c = ISLOWER(c) ? 797 CCHR(TOUPPER(c)) : CCHR(c); 798 break; 799 case '0': 800 case '1': 801 case '2': 802 case '3': 803 case '4': 804 case '5': 805 case '6': 806 case '7': 807 c = *argp - '0'; 808 if (argp[1] <= '7' && 809 argp[1] >= '0') { 810 c <<= 3; 811 c += *++argp - '0'; 812 if (argp[1] <= '7' && 813 argp[1] >= '0') { 814 c <<= 3; 815 c += *++argp 816 - '0'; 817 } 818 } 819 break; 820 case 'f': 821 case 'F': 822 c = *++argp - '0'; 823 if (ISDIGIT(argp[1])) { 824 c *= 10; 825 c += *++argp - '0'; 826 } 827 c += KFIRST; 828 break; 829 default: 830 c = CHARMASK(*argp); 831 break; 832 } 833 argp++; 834 } 835 if (bind == BINDARG) 836 key.k_chars[key.k_count++] = c; 837 else 838 lp->l_text[lp->l_used++] = c; 839 } 840 if (*line) 841 line++; 842 } 843 switch (bind) { 844 case BINDARG: 845 bind = BINDDO; 846 break; 847 case BINDNEXT: 848 lp->l_text[lp->l_used] = '\0'; 849 if ((curmap = name_map(lp->l_text)) == NULL) { 850 (void)dobeep_msgs("No such mode:", lp->l_text); 851 status = FALSE; 852 free(lp); 853 goto cleanup; 854 } 855 free(lp); 856 bind = BINDARG; 857 break; 858 default: 859 lp->l_fp = np->l_fp; 860 lp->l_bp = np; 861 np->l_fp = lp; 862 np = lp; 863 } 864 } 865 switch (bind) { 866 default: 867 (void)dobeep_msg("Bad args to set key"); 868 status = FALSE; 869 break; 870 case BINDDO: 871 if (fp != unbindtokey && fp != localunbind) { 872 lp->l_text[lp->l_used] = '\0'; 873 status = bindkey(&curmap, lp->l_text, key.k_chars, 874 key.k_count); 875 } else 876 status = bindkey(&curmap, NULL, key.k_chars, 877 key.k_count); 878 break; 879 case BINDNO: 880 inmacro = TRUE; 881 maclcur = maclcur->l_fp; 882 status = (*fp)(f, n); 883 inmacro = FALSE; 884 } 885 cleanup: 886 lp = maclcur->l_fp; 887 while (lp != maclcur) { 888 np = lp->l_fp; 889 free(lp); 890 lp = np; 891 } 892 free(lp); 893 maclhead = NULL; 894 macrodef = FALSE; 895 return (status); 896 } 897 898 /* 899 * a pair of utility functions for the above 900 */ 901 char * 902 skipwhite(char *s) 903 { 904 while (*s == ' ' || *s == '\t') 905 s++; 906 if ((*s == ';') || (*s == '#')) 907 *s = '\0'; 908 return (s); 909 } 910 911 static char * 912 parsetoken(char *s) 913 { 914 if (*s != '"') { 915 while (*s && *s != ' ' && *s != '\t' && *s != ')' && *s != '(') 916 s++; 917 if (*s == ';') 918 *s = '\0'; 919 } else 920 do { 921 /* 922 * Strings get special treatment. 923 * Beware: You can \ out the end of the string! 924 */ 925 if (*s == '\\') 926 ++s; 927 } while (*++s != '"' && *s != '\0'); 928 return (s); 929 } 930