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