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