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