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