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