1 /* $OpenBSD: extend.c,v 1.50 2006/12/30 14:11:06 martin 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 <sys/types.h> 9 #include <ctype.h> 10 11 #include "chrdef.h" 12 #include "def.h" 13 #include "kbd.h" 14 #include "funmap.h" 15 16 #ifndef NO_MACRO 17 #include "macro.h" 18 #endif /* !NO_MACRO */ 19 20 #ifdef FKEYS 21 #include "key.h" 22 #ifndef NO_STARTUP 23 #ifndef BINDKEY 24 #define BINDKEY /* bindkey is used by FKEYS startup code */ 25 #endif /* !BINDKEY */ 26 #endif /* !NO_STARTUP */ 27 #endif /* FKEYS */ 28 29 #include <ctype.h> 30 31 static int remap(KEYMAP *, int, PF, KEYMAP *); 32 static KEYMAP *reallocmap(KEYMAP *); 33 static void fixmap(KEYMAP *, KEYMAP *, KEYMAP *); 34 static int dobind(KEYMAP *, const char *, int); 35 static char *skipwhite(char *); 36 static char *parsetoken(char *); 37 #ifdef BINDKEY 38 static int bindkey(KEYMAP **, const char *, KCHAR *, int); 39 #endif /* BINDKEY */ 40 41 /* 42 * Insert a string, mainly for use from macros (created by selfinsert). 43 */ 44 /* ARGSUSED */ 45 int 46 insert(int f, int n) 47 { 48 char buf[128], *bufp, *cp; 49 #ifndef NO_MACRO 50 int count, c; 51 52 if (inmacro) { 53 while (--n >= 0) { 54 for (count = 0; count < maclcur->l_used; count++) { 55 if ((((c = maclcur->l_text[count]) == '\n') 56 ? lnewline() : linsert(1, c)) != TRUE) 57 return (FALSE); 58 } 59 } 60 maclcur = maclcur->l_fp; 61 return (TRUE); 62 } 63 if (n == 1) 64 /* CFINS means selfinsert can tack on the end */ 65 thisflag |= CFINS; 66 #endif /* !NO_MACRO */ 67 if ((bufp = eread("Insert: ", buf, sizeof(buf), EFNEW)) == NULL) 68 return (ABORT); 69 else if (bufp[0] == '\0') 70 return (FALSE); 71 while (--n >= 0) { 72 cp = buf; 73 while (*cp) { 74 if (((*cp == '\n') ? lnewline() : linsert(1, *cp)) 75 != TRUE) 76 return (FALSE); 77 cp++; 78 } 79 } 80 return (TRUE); 81 } 82 83 /* 84 * Bind a key to a function. Cases range from the trivial (replacing an 85 * existing binding) to the extremely complex (creating a new prefix in a 86 * map_element that already has one, so the map_element must be split, 87 * but the keymap doesn't have enough room for another map_element, so 88 * the keymap is reallocated). No attempt is made to reclaim space no 89 * longer used, if this is a problem flags must be added to indicate 90 * malloced versus static storage in both keymaps and map_elements. 91 * Structure assignments would come in real handy, but K&R based compilers 92 * don't have them. Care is taken so running out of memory will leave 93 * the keymap in a usable state. 94 * Parameters are: 95 * curmap: pointer to the map being changed 96 * c: character being changed 97 * funct: function being changed to 98 * pref_map: if funct==NULL, map to bind to or NULL for new 99 */ 100 static int 101 remap(KEYMAP *curmap, int c, PF funct, KEYMAP *pref_map) 102 { 103 int i, n1, n2, nold; 104 KEYMAP *mp, *newmap; 105 PF *pfp; 106 struct map_element *mep; 107 108 if (ele >= &curmap->map_element[curmap->map_num] || c < ele->k_base) { 109 if (ele > &curmap->map_element[0] && (funct != NULL || 110 (ele - 1)->k_prefmap == NULL)) 111 n1 = c - (ele - 1)->k_num; 112 else 113 n1 = HUGE; 114 if (ele < &curmap->map_element[curmap->map_num] && 115 (funct != NULL || ele->k_prefmap == NULL)) 116 n2 = ele->k_base - c; 117 else 118 n2 = HUGE; 119 if (n1 <= MAPELEDEF && n1 <= n2) { 120 ele--; 121 if ((pfp = calloc(c - ele->k_base + 1, 122 sizeof(PF))) == NULL) { 123 ewprintf("Out of memory"); 124 return (FALSE); 125 } 126 nold = ele->k_num - ele->k_base + 1; 127 for (i = 0; i < nold; i++) 128 pfp[i] = ele->k_funcp[i]; 129 while (--n1) 130 pfp[i++] = curmap->map_default; 131 pfp[i] = funct; 132 ele->k_num = c; 133 ele->k_funcp = pfp; 134 } else if (n2 <= MAPELEDEF) { 135 if ((pfp = calloc(ele->k_num - c + 1, 136 sizeof(PF))) == NULL) { 137 ewprintf("Out of memory"); 138 return (FALSE); 139 } 140 nold = ele->k_num - ele->k_base + 1; 141 for (i = 0; i < nold; i++) 142 pfp[i + n2] = ele->k_funcp[i]; 143 while (--n2) 144 pfp[n2] = curmap->map_default; 145 pfp[0] = funct; 146 ele->k_base = c; 147 ele->k_funcp = pfp; 148 } else { 149 if (curmap->map_num >= curmap->map_max) { 150 if ((newmap = reallocmap(curmap)) == NULL) 151 return (FALSE); 152 curmap = newmap; 153 } 154 if ((pfp = malloc(sizeof(PF))) == NULL) { 155 ewprintf("Out of memory"); 156 return (FALSE); 157 } 158 pfp[0] = funct; 159 for (mep = &curmap->map_element[curmap->map_num]; 160 mep > ele; mep--) { 161 mep->k_base = (mep - 1)->k_base; 162 mep->k_num = (mep - 1)->k_num; 163 mep->k_funcp = (mep - 1)->k_funcp; 164 mep->k_prefmap = (mep - 1)->k_prefmap; 165 } 166 ele->k_base = c; 167 ele->k_num = c; 168 ele->k_funcp = pfp; 169 ele->k_prefmap = NULL; 170 curmap->map_num++; 171 } 172 if (funct == NULL) { 173 if (pref_map != NULL) 174 ele->k_prefmap = pref_map; 175 else { 176 if ((mp = malloc(sizeof(KEYMAP) + 177 (MAPINIT - 1) * sizeof(struct map_element))) == NULL) { 178 ewprintf("Out of memory"); 179 ele->k_funcp[c - ele->k_base] = 180 curmap->map_default; 181 return (FALSE); 182 } 183 mp->map_num = 0; 184 mp->map_max = MAPINIT; 185 mp->map_default = rescan; 186 ele->k_prefmap = mp; 187 } 188 } 189 } else { 190 n1 = c - ele->k_base; 191 if (ele->k_funcp[n1] == funct && (funct != NULL || 192 pref_map == NULL || pref_map == ele->k_prefmap)) 193 /* no change */ 194 return (TRUE); 195 if (funct != NULL || ele->k_prefmap == NULL) { 196 if (ele->k_funcp[n1] == NULL) 197 ele->k_prefmap = NULL; 198 /* easy case */ 199 ele->k_funcp[n1] = funct; 200 if (funct == NULL) { 201 if (pref_map != NULL) 202 ele->k_prefmap = pref_map; 203 else { 204 if ((mp = malloc(sizeof(KEYMAP) + 205 (MAPINIT - 1) * 206 sizeof(struct map_element))) == NULL) { 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 ewprintf("Out of memory"); 235 return (FALSE); 236 } 237 ele->k_funcp[n1] = NULL; 238 for (i = n1 + n2; i <= ele->k_num - ele->k_base; i++) 239 pfp[i - n1 - n2] = ele->k_funcp[i]; 240 for (mep = &curmap->map_element[curmap->map_num]; 241 mep > ele; mep--) { 242 mep->k_base = (mep - 1)->k_base; 243 mep->k_num = (mep - 1)->k_num; 244 mep->k_funcp = (mep - 1)->k_funcp; 245 mep->k_prefmap = (mep - 1)->k_prefmap; 246 } 247 ele->k_num = c - !n2; 248 (ele + 1)->k_base = c + n2; 249 (ele + 1)->k_funcp = pfp; 250 ele += !n2; 251 ele->k_prefmap = NULL; 252 curmap->map_num++; 253 if (pref_map == NULL) { 254 if ((mp = malloc(sizeof(KEYMAP) + (MAPINIT - 1) 255 * sizeof(struct map_element))) == NULL) { 256 ewprintf("Out of memory"); 257 ele->k_funcp[c - ele->k_base] = 258 curmap->map_default; 259 return (FALSE); 260 } 261 mp->map_num = 0; 262 mp->map_max = MAPINIT; 263 mp->map_default = rescan; 264 ele->k_prefmap = mp; 265 } else 266 ele->k_prefmap = pref_map; 267 } 268 } 269 return (TRUE); 270 } 271 272 /* 273 * Reallocate a keymap. Returns NULL (without trashing the current map) 274 * on failure. 275 */ 276 static KEYMAP * 277 reallocmap(KEYMAP *curmap) 278 { 279 struct maps_s *mps; 280 KEYMAP *mp; 281 int i; 282 283 if (curmap->map_max > SHRT_MAX - MAPGROW) { 284 ewprintf("keymap too large"); 285 return (NULL); 286 } 287 if ((mp = malloc(sizeof(KEYMAP) + (curmap->map_max + (MAPGROW - 1)) * 288 sizeof(struct map_element))) == NULL) { 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 #ifndef NO_MACRO 342 if (macrodef) { 343 /* 344 * Keystrokes aren't collected. Not hard, but pretty useless. 345 * Would not work for function keys in any case. 346 */ 347 ewprintf("Can't rebind key in macro"); 348 return (FALSE); 349 } 350 #ifndef NO_STARTUP 351 if (inmacro) { 352 for (s = 0; s < maclcur->l_used - 1; s++) { 353 if (doscan(curmap, c = CHARMASK(maclcur->l_text[s]), &curmap) 354 != NULL) { 355 if (remap(curmap, c, NULL, NULL) 356 != TRUE) 357 return (FALSE); 358 } 359 } 360 (void)doscan(curmap, c = maclcur->l_text[s], NULL); 361 maclcur = maclcur->l_fp; 362 } else { 363 #endif /* !NO_STARTUP */ 364 #endif /* !NO_MACRO */ 365 n = strlcpy(bprompt, p, sizeof(bprompt)); 366 if (n >= sizeof(bprompt)) 367 n = sizeof(bprompt) - 1; 368 pep = bprompt + n; 369 for (;;) { 370 ewprintf("%s", bprompt); 371 pep[-1] = ' '; 372 pep = getkeyname(pep, sizeof(bprompt) - 373 (pep - bprompt), c = getkey(FALSE)); 374 if (doscan(curmap, c, &curmap) != NULL) 375 break; 376 *pep++ = '-'; 377 *pep = '\0'; 378 } 379 #ifndef NO_STARTUP 380 } 381 #endif /* !NO_STARTUP */ 382 if (unbind) 383 funct = rescan; 384 else { 385 if ((bufp = eread("%s to command: ", bprompt, sizeof(bprompt), 386 EFFUNC | EFNEW, bprompt)) == NULL) 387 return (ABORT); 388 else if (bufp[0] == '\0') 389 return (FALSE); 390 if (((funct = name_function(bprompt)) == NULL) ? 391 (pref_map = name_map(bprompt)) == NULL : funct == NULL) { 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 ewprintf("[No match: %s]", fname); 419 return (FALSE); 420 } 421 while (--kcount) { 422 if (doscan(curmap, c = *keys++, &curmap) != NULL) { 423 if (remap(curmap, c, NULL, NULL) != TRUE) 424 return (FALSE); 425 /* 426 * XXX - Bizzarreness. remap creates an empty KEYMAP 427 * that the last key is supposed to point to. 428 */ 429 curmap = ele->k_prefmap; 430 } 431 } 432 (void)doscan(curmap, c = *keys, NULL); 433 return (remap(curmap, c, funct, pref_map)); 434 } 435 436 #ifdef FKEYS 437 /* 438 * Wrapper for bindkey() that converts escapes. 439 */ 440 int 441 dobindkey(KEYMAP *map, const char *func, const char *str) 442 { 443 int i; 444 445 for (i = 0; *str && i < MAXKEY; i++) { 446 /* XXX - convert numbers w/ strol()? */ 447 if (*str == '^' && *(str + 1) != '\0') { 448 key.k_chars[i] = CCHR(toupper(*++str)); 449 } else if (*str == '\\' && *(str + 1) != '\0') { 450 switch (*++str) { 451 case '^': 452 key.k_chars[i] = '^'; 453 break; 454 case 't': 455 case 'T': 456 key.k_chars[i] = '\t'; 457 break; 458 case 'n': 459 case 'N': 460 key.k_chars[i] = '\n'; 461 break; 462 case 'r': 463 case 'R': 464 key.k_chars[i] = '\r'; 465 break; 466 case 'e': 467 case 'E': 468 key.k_chars[i] = CCHR('['); 469 break; 470 case '\\': 471 key.k_chars[i] = '\\'; 472 break; 473 } 474 } else 475 key.k_chars[i] = *str; 476 str++; 477 } 478 key.k_count = i; 479 return (bindkey(&map, func, key.k_chars, key.k_count)); 480 } 481 #endif /* FKEYS */ 482 #endif /* BINDKEY */ 483 484 /* 485 * This function modifies the fundamental keyboard map. 486 */ 487 /* ARGSUSED */ 488 int 489 bindtokey(int f, int n) 490 { 491 return (dobind(fundamental_map, "Global set key: ", FALSE)); 492 } 493 494 /* 495 * This function modifies the current mode's keyboard map. 496 */ 497 /* ARGSUSED */ 498 int 499 localbind(int f, int n) 500 { 501 return (dobind(curbp->b_modes[curbp->b_nmodes]->p_map, 502 "Local set key: ", FALSE)); 503 } 504 505 /* 506 * This function redefines a key in any keymap. 507 */ 508 /* ARGSUSED */ 509 int 510 redefine_key(int f, int n) 511 { 512 static char buf[48]; 513 char tmp[32], *bufp; 514 KEYMAP *mp; 515 516 (void)strlcpy(buf, "Define key map: ", sizeof(buf)); 517 if ((bufp = eread(buf, tmp, sizeof(tmp), EFNEW)) == NULL) 518 return (ABORT); 519 else if (bufp[0] == '\0') 520 return (FALSE); 521 (void)strlcat(buf, tmp, sizeof(buf)); 522 if ((mp = name_map(tmp)) == NULL) { 523 ewprintf("Unknown map %s", tmp); 524 return (FALSE); 525 } 526 if (strlcat(buf, "key: ", sizeof(buf)) >= sizeof(buf)) 527 return (FALSE); 528 529 return (dobind(mp, buf, FALSE)); 530 } 531 532 /* ARGSUSED */ 533 int 534 unbindtokey(int f, int n) 535 { 536 return (dobind(fundamental_map, "Global unset key: ", TRUE)); 537 } 538 539 /* ARGSUSED */ 540 int 541 localunbind(int f, int n) 542 { 543 return (dobind(curbp->b_modes[curbp->b_nmodes]->p_map, 544 "Local unset key: ", TRUE)); 545 } 546 547 /* 548 * Extended command. Call the message line routine to read in the command 549 * name and apply autocompletion to it. When it comes back, look the name 550 * up in the symbol table and run the command if it is found. Print an 551 * error if there is anything wrong. 552 */ 553 int 554 extend(int f, int n) 555 { 556 PF funct; 557 char xname[NXNAME], *bufp; 558 559 if (!(f & FFARG)) 560 bufp = eread("M-x ", xname, NXNAME, EFNEW | EFFUNC); 561 else 562 bufp = eread("%d M-x ", xname, NXNAME, EFNEW | EFFUNC, n); 563 if (bufp == NULL) 564 return (ABORT); 565 else if (bufp[0] == '\0') 566 return (FALSE); 567 if ((funct = name_function(bufp)) != NULL) { 568 #ifndef NO_MACRO 569 if (macrodef) { 570 struct line *lp = maclcur; 571 macro[macrocount - 1].m_funct = funct; 572 maclcur = lp->l_bp; 573 maclcur->l_fp = lp->l_fp; 574 free(lp); 575 } 576 #endif /* !NO_MACRO */ 577 return ((*funct)(f, n)); 578 } 579 ewprintf("[No match]"); 580 return (FALSE); 581 } 582 583 #ifndef NO_STARTUP 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 670 if ((fname = adjustname(fname, TRUE)) == NULL) 671 /* just to be careful */ 672 return (FALSE); 673 674 if (ffropen(fname, NULL) != FIOSUC) 675 return (FALSE); 676 677 line = 0; 678 while ((s = ffgetline(excbuf, sizeof(excbuf) - 1, &nbytes)) == FIOSUC) { 679 line++; 680 excbuf[nbytes] = '\0'; 681 if (excline(excbuf) != TRUE) { 682 s = FIOERR; 683 ewprintf("Error loading file %s at line %d", fname, line); 684 break; 685 } 686 } 687 (void)ffclose(NULL); 688 excbuf[nbytes] = '\0'; 689 if (s != FIOEOF || (nbytes && excline(excbuf) != TRUE)) 690 return (FALSE); 691 return (TRUE); 692 } 693 694 /* 695 * excline - run a line from a load file or eval-expression. If FKEYS is 696 * defined, duplicate functionality of dobind so function key values don't 697 * have to fit in type char. 698 */ 699 int 700 excline(char *line) 701 { 702 PF fp; 703 struct line *lp, *np; 704 int status, c, f, n; 705 char *funcp, *tmp; 706 char *argp = NULL; 707 long nl; 708 #ifdef FKEYS 709 int bind; 710 KEYMAP *curmap; 711 #define BINDARG 0 /* this arg is key to bind (local/global set key) */ 712 #define BINDNO 1 /* not binding or non-quoted BINDARG */ 713 #define BINDNEXT 2 /* next arg " (define-key) */ 714 #define BINDDO 3 /* already found key to bind */ 715 #define BINDEXT 1 /* space for trailing \0 */ 716 #else /* FKEYS */ 717 #define BINDEXT 0 718 #endif /* FKEYS */ 719 720 lp = NULL; 721 722 if (macrodef || inmacro) { 723 ewprintf("Not now!"); 724 return (FALSE); 725 } 726 f = 0; 727 n = 1; 728 funcp = skipwhite(line); 729 if (*funcp == '\0') 730 return (TRUE); /* No error on blank lines */ 731 line = parsetoken(funcp); 732 if (*line != '\0') { 733 *line++ = '\0'; 734 line = skipwhite(line); 735 if (ISDIGIT(*line) || *line == '-') { 736 argp = line; 737 line = parsetoken(line); 738 } 739 } 740 if (argp != NULL) { 741 f = FFARG; 742 nl = strtol(argp, &tmp, 10); 743 if (*tmp != '\0') 744 return (FALSE); 745 if (nl >= INT_MAX || nl <= INT_MIN) 746 return (FALSE); 747 n = (int)nl; 748 } 749 if ((fp = name_function(funcp)) == NULL) { 750 ewprintf("Unknown function: %s", funcp); 751 return (FALSE); 752 } 753 #ifdef FKEYS 754 if (fp == bindtokey || fp == unbindtokey) { 755 bind = BINDARG; 756 curmap = fundamental_map; 757 } else if (fp == localbind || fp == localunbind) { 758 bind = BINDARG; 759 curmap = curbp->b_modes[curbp->b_nmodes]->p_map; 760 } else if (fp == redefine_key) 761 bind = BINDNEXT; 762 else 763 bind = BINDNO; 764 #endif /* FKEYS */ 765 /* Pack away all the args now... */ 766 if ((np = lalloc(0)) == FALSE) 767 return (FALSE); 768 np->l_fp = np->l_bp = maclcur = np; 769 while (*line != '\0') { 770 argp = skipwhite(line); 771 if (*argp == '\0') 772 break; 773 line = parsetoken(argp); 774 if (*argp != '"') { 775 if (*argp == '\'') 776 ++argp; 777 if ((lp = lalloc((int) (line - argp) + BINDEXT)) == 778 NULL) { 779 status = FALSE; 780 goto cleanup; 781 } 782 bcopy(argp, ltext(lp), (int)(line - argp)); 783 #ifdef FKEYS 784 /* don't count BINDEXT */ 785 lp->l_used--; 786 if (bind == BINDARG) 787 bind = BINDNO; 788 #endif /* FKEYS */ 789 } else { 790 /* quoted strings are special */ 791 ++argp; 792 #ifdef FKEYS 793 if (bind != BINDARG) { 794 #endif /* FKEYS */ 795 lp = lalloc((int)(line - argp) + BINDEXT); 796 if (lp == NULL) { 797 status = FALSE; 798 goto cleanup; 799 } 800 lp->l_used = 0; 801 #ifdef FKEYS 802 } else 803 key.k_count = 0; 804 #endif /* FKEYS */ 805 while (*argp != '"' && *argp != '\0') { 806 if (*argp != '\\') 807 c = *argp++; 808 else { 809 switch (*++argp) { 810 case 't': 811 case 'T': 812 c = CCHR('I'); 813 break; 814 case 'n': 815 case 'N': 816 c = CCHR('J'); 817 break; 818 case 'r': 819 case 'R': 820 c = CCHR('M'); 821 break; 822 case 'e': 823 case 'E': 824 c = CCHR('['); 825 break; 826 case '^': 827 /* 828 * split into two statements 829 * due to bug in OSK cpp 830 */ 831 c = CHARMASK(*++argp); 832 c = ISLOWER(c) ? 833 CCHR(TOUPPER(c)) : CCHR(c); 834 break; 835 case '0': 836 case '1': 837 case '2': 838 case '3': 839 case '4': 840 case '5': 841 case '6': 842 case '7': 843 c = *argp - '0'; 844 if (argp[1] <= '7' && 845 argp[1] >= '0') { 846 c <<= 3; 847 c += *++argp - '0'; 848 if (argp[1] <= '7' && 849 argp[1] >= '0') { 850 c <<= 3; 851 c += *++argp 852 - '0'; 853 } 854 } 855 break; 856 #ifdef FKEYS 857 case 'f': 858 case 'F': 859 c = *++argp - '0'; 860 if (ISDIGIT(argp[1])) { 861 c *= 10; 862 c += *++argp - '0'; 863 } 864 c += KFIRST; 865 break; 866 #endif /* FKEYS */ 867 default: 868 c = CHARMASK(*argp); 869 break; 870 } 871 argp++; 872 } 873 #ifdef FKEYS 874 if (bind == BINDARG) 875 key.k_chars[key.k_count++] = c; 876 else 877 #endif /* FKEYS */ 878 lp->l_text[lp->l_used++] = c; 879 } 880 if (*line) 881 line++; 882 } 883 #ifdef FKEYS 884 switch (bind) { 885 case BINDARG: 886 bind = BINDDO; 887 break; 888 case BINDNEXT: 889 lp->l_text[lp->l_used] = '\0'; 890 if ((curmap = name_map(lp->l_text)) == NULL) { 891 ewprintf("No such mode: %s", lp->l_text); 892 status = FALSE; 893 free(lp); 894 goto cleanup; 895 } 896 free(lp); 897 bind = BINDARG; 898 break; 899 default: 900 #endif /* FKEYS */ 901 lp->l_fp = np->l_fp; 902 lp->l_bp = np; 903 np->l_fp = lp; 904 np = lp; 905 #ifdef FKEYS 906 } 907 #endif /* FKEYS */ 908 } 909 #ifdef FKEYS 910 switch (bind) { 911 default: 912 ewprintf("Bad args to set key"); 913 status = FALSE; 914 break; 915 case BINDDO: 916 if (fp != unbindtokey && fp != localunbind) { 917 lp->l_text[lp->l_used] = '\0'; 918 status = bindkey(&curmap, lp->l_text, key.k_chars, 919 key.k_count); 920 } else 921 status = bindkey(&curmap, NULL, key.k_chars, 922 key.k_count); 923 break; 924 case BINDNO: 925 #endif /* FKEYS */ 926 inmacro = TRUE; 927 maclcur = maclcur->l_fp; 928 status = (*fp)(f, n); 929 inmacro = FALSE; 930 #ifdef FKEYS 931 } 932 #endif /* FKEYS */ 933 cleanup: 934 lp = maclcur->l_fp; 935 while (lp != maclcur) { 936 np = lp->l_fp; 937 free(lp); 938 lp = np; 939 } 940 free(lp); 941 return (status); 942 } 943 944 /* 945 * a pair of utility functions for the above 946 */ 947 static char * 948 skipwhite(char *s) 949 { 950 while (*s == ' ' || *s == '\t' || *s == ')' || *s == '(') 951 s++; 952 if (*s == ';') 953 *s = '\0'; 954 return (s); 955 } 956 957 static char * 958 parsetoken(char *s) 959 { 960 if (*s != '"') { 961 while (*s && *s != ' ' && *s != '\t' && *s != ')' && *s != '(') 962 s++; 963 if (*s == ';') 964 *s = '\0'; 965 } else 966 do { 967 /* 968 * Strings get special treatment. 969 * Beware: You can \ out the end of the string! 970 */ 971 if (*s == '\\') 972 ++s; 973 } while (*++s != '"' && *s != '\0'); 974 return (s); 975 } 976 #endif /* !NO_STARTUP */ 977