1 /* $OpenBSD: extend.c,v 1.75 2021/05/06 14:16:12 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, 1)); 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, lnum = 0; 604 static char excbuf[BUFSIZE]; 605 606 for (lp = bfirstlp(bp); lp != bp->b_headp; lp = lforw(lp)) { 607 lnum++; 608 llen = llength(lp); 609 if (llen >= BUFSIZE) 610 return (FALSE); 611 (void)strncpy(excbuf, ltext(lp), llen); 612 613 /* make sure the line is terminated */ 614 excbuf[llen] = '\0'; 615 if ((s = excline(excbuf, llen, lnum)) != TRUE) { 616 cleanup(); 617 return (s); 618 } 619 } 620 cleanup(); 621 return (TRUE); 622 } 623 624 /* 625 * evalfile - go get a file and evaluate it as line commands. You can 626 * go get your own startup file if need be. 627 */ 628 /* ARGSUSED */ 629 int 630 evalfile(int f, int n) 631 { 632 char fname[NFILEN], *bufp; 633 634 if ((bufp = eread("Load file: ", fname, NFILEN, 635 EFNEW | EFCR)) == NULL) 636 return (ABORT); 637 else if (bufp[0] == '\0') 638 return (FALSE); 639 return (load(fname)); 640 } 641 642 /* 643 * load - go load the file name we got passed. 644 */ 645 int 646 load(const char *fname) 647 { 648 int s = TRUE, line, ret; 649 int nbytes = 0; 650 char excbuf[BUFSIZE], fncpy[NFILEN]; 651 FILE *ffp; 652 653 if ((fname = adjustname(fname, TRUE)) == NULL) 654 /* just to be careful */ 655 return (FALSE); 656 657 ret = ffropen(&ffp, fname, NULL); 658 if (ret != FIOSUC) { 659 if (ret == FIODIR) 660 (void)ffclose(ffp, NULL); 661 return (FALSE); 662 } 663 664 /* keep a note of fname incase of errors in loaded file. */ 665 (void)strlcpy(fncpy, fname, sizeof(fncpy)); 666 line = 0; 667 while ((s = ffgetline(ffp, excbuf, sizeof(excbuf) - 1, &nbytes)) 668 == FIOSUC) { 669 line++; 670 excbuf[nbytes] = '\0'; 671 if (excline(excbuf, nbytes, line) != TRUE) { 672 s = FIOERR; 673 dobeep(); 674 ewprintf("Error loading file %s at line %d", fncpy, line); 675 break; 676 } 677 } 678 (void)ffclose(ffp, NULL); 679 excbuf[nbytes] = '\0'; 680 if (s != FIOEOF || (nbytes && excline(excbuf, nbytes, ++line) != TRUE)) 681 return (FALSE); 682 return (TRUE); 683 } 684 685 /* 686 * excline - run a line from a load file or eval-expression. 687 */ 688 int 689 excline(char *line, int llen, int lnum) 690 { 691 PF fp; 692 struct line *lp, *np; 693 int status, c, f, n; 694 char *funcp, *tmp; 695 char *argp = NULL; 696 long nl; 697 int bind; 698 KEYMAP *curmap; 699 #define BINDARG 0 /* this arg is key to bind (local/global set key) */ 700 #define BINDNO 1 /* not binding or non-quoted BINDARG */ 701 #define BINDNEXT 2 /* next arg " (define-key) */ 702 #define BINDDO 3 /* already found key to bind */ 703 #define BINDEXT 1 /* space for trailing \0 */ 704 705 lp = NULL; 706 707 if (macrodef || inmacro) 708 return (dobeep_msg("Not now!")); 709 710 f = 0; 711 n = 1; 712 funcp = skipwhite(line); 713 if (*funcp == '\0') 714 return (TRUE); /* No error on blank lines */ 715 if (*funcp == '(') 716 return (foundparen(funcp, llen, lnum)); 717 line = parsetoken(funcp); 718 if (*line != '\0') { 719 *line++ = '\0'; 720 line = skipwhite(line); 721 if (ISDIGIT(*line) || *line == '-') { 722 argp = line; 723 line = parsetoken(line); 724 } 725 } 726 if (argp != NULL) { 727 f = FFARG; 728 nl = strtol(argp, &tmp, 10); 729 if (*tmp != '\0') 730 return (FALSE); 731 if (nl >= INT_MAX || nl <= INT_MIN) 732 return (FALSE); 733 n = (int)nl; 734 } 735 if ((fp = name_function(funcp)) == NULL) 736 return (dobeep_msgs("Unknown function: ", funcp)); 737 738 if (fp == bindtokey || fp == unbindtokey) { 739 bind = BINDARG; 740 curmap = fundamental_map; 741 } else if (fp == localbind || fp == localunbind) { 742 bind = BINDARG; 743 curmap = curbp->b_modes[curbp->b_nmodes]->p_map; 744 } else if (fp == redefine_key) 745 bind = BINDNEXT; 746 else 747 bind = BINDNO; 748 /* Pack away all the args now... */ 749 if ((np = lalloc(0)) == FALSE) 750 return (FALSE); 751 np->l_fp = np->l_bp = maclcur = np; 752 while (*line != '\0') { 753 argp = skipwhite(line); 754 if (*argp == '\0') 755 break; 756 line = parsetoken(argp); 757 if (*argp != '"') { 758 if (*argp == '\'') 759 ++argp; 760 if ((lp = lalloc((int) (line - argp) + BINDEXT)) == 761 NULL) { 762 status = FALSE; 763 goto cleanup; 764 } 765 bcopy(argp, ltext(lp), (int)(line - argp)); 766 /* don't count BINDEXT */ 767 lp->l_used--; 768 if (bind == BINDARG) 769 bind = BINDNO; 770 } else { 771 /* quoted strings are special */ 772 ++argp; 773 if (bind != BINDARG) { 774 lp = lalloc((int)(line - argp) + BINDEXT); 775 if (lp == NULL) { 776 status = FALSE; 777 goto cleanup; 778 } 779 lp->l_used = 0; 780 } else 781 key.k_count = 0; 782 while (*argp != '"' && *argp != '\0') { 783 if (*argp != '\\') 784 c = *argp++; 785 else { 786 switch (*++argp) { 787 case 't': 788 case 'T': 789 c = CCHR('I'); 790 break; 791 case 'n': 792 case 'N': 793 c = CCHR('J'); 794 break; 795 case 'r': 796 case 'R': 797 c = CCHR('M'); 798 break; 799 case 'e': 800 case 'E': 801 c = CCHR('['); 802 break; 803 case '^': 804 /* 805 * split into two statements 806 * due to bug in OSK cpp 807 */ 808 c = CHARMASK(*++argp); 809 c = ISLOWER(c) ? 810 CCHR(TOUPPER(c)) : CCHR(c); 811 break; 812 case '0': 813 case '1': 814 case '2': 815 case '3': 816 case '4': 817 case '5': 818 case '6': 819 case '7': 820 c = *argp - '0'; 821 if (argp[1] <= '7' && 822 argp[1] >= '0') { 823 c <<= 3; 824 c += *++argp - '0'; 825 if (argp[1] <= '7' && 826 argp[1] >= '0') { 827 c <<= 3; 828 c += *++argp 829 - '0'; 830 } 831 } 832 break; 833 case 'f': 834 case 'F': 835 c = *++argp - '0'; 836 if (ISDIGIT(argp[1])) { 837 c *= 10; 838 c += *++argp - '0'; 839 } 840 c += KFIRST; 841 break; 842 default: 843 c = CHARMASK(*argp); 844 break; 845 } 846 argp++; 847 } 848 if (bind == BINDARG) 849 key.k_chars[key.k_count++] = c; 850 else 851 lp->l_text[lp->l_used++] = c; 852 } 853 if (*line) 854 line++; 855 } 856 switch (bind) { 857 case BINDARG: 858 bind = BINDDO; 859 break; 860 case BINDNEXT: 861 lp->l_text[lp->l_used] = '\0'; 862 if ((curmap = name_map(lp->l_text)) == NULL) { 863 (void)dobeep_msgs("No such mode: ", lp->l_text); 864 status = FALSE; 865 free(lp); 866 goto cleanup; 867 } 868 free(lp); 869 bind = BINDARG; 870 break; 871 default: 872 lp->l_fp = np->l_fp; 873 lp->l_bp = np; 874 np->l_fp = lp; 875 np = lp; 876 } 877 } 878 switch (bind) { 879 default: 880 (void)dobeep_msg("Bad args to set key"); 881 status = FALSE; 882 break; 883 case BINDDO: 884 if (fp != unbindtokey && fp != localunbind) { 885 lp->l_text[lp->l_used] = '\0'; 886 status = bindkey(&curmap, lp->l_text, key.k_chars, 887 key.k_count); 888 } else 889 status = bindkey(&curmap, NULL, key.k_chars, 890 key.k_count); 891 break; 892 case BINDNO: 893 inmacro = TRUE; 894 maclcur = maclcur->l_fp; 895 status = (*fp)(f, n); 896 inmacro = FALSE; 897 } 898 cleanup: 899 lp = maclcur->l_fp; 900 while (lp != maclcur) { 901 np = lp->l_fp; 902 free(lp); 903 lp = np; 904 } 905 free(lp); 906 maclhead = NULL; 907 macrodef = FALSE; 908 return (status); 909 } 910 911 /* 912 * a pair of utility functions for the above 913 */ 914 char * 915 skipwhite(char *s) 916 { 917 while (*s == ' ' || *s == '\t') 918 s++; 919 if ((*s == ';') || (*s == '#')) 920 *s = '\0'; 921 return (s); 922 } 923 924 static char * 925 parsetoken(char *s) 926 { 927 if (*s != '"') { 928 while (*s && *s != ' ' && *s != '\t' && *s != ')' && *s != '(') 929 s++; 930 if (*s == ';') 931 *s = '\0'; 932 } else 933 do { 934 /* 935 * Strings get special treatment. 936 * Beware: You can \ out the end of the string! 937 */ 938 if (*s == '\\') 939 ++s; 940 } while (*++s != '"' && *s != '\0'); 941 return (s); 942 } 943