|
|
1.1 ! root 1: /* tio.c */ ! 2: ! 3: /* Author: ! 4: * Steve Kirkendall ! 5: * 14407 SW Teal Blvd. #C ! 6: * Beaverton, OR 97005 ! 7: * [email protected] ! 8: */ ! 9: ! 10: ! 11: /* This file contains terminal I/O functions */ ! 12: ! 13: #include "config.h" ! 14: #include "vi.h" ! 15: #include "ctype.h" ! 16: ! 17: static int showmsg P_((void)); ! 18: ! 19: /* This function reads in a line from the terminal. */ ! 20: int vgets(prompt, buf, bsize) ! 21: int prompt; /* the prompt character, or '\0' for none */ ! 22: char *buf; /* buffer into which the string is read */ ! 23: int bsize; /* size of the buffer */ ! 24: { ! 25: int len; /* how much we've read so far */ ! 26: int ch; /* a character from the user */ ! 27: int quoted; /* is the next char quoted? */ ! 28: int tab; /* column position of cursor */ ! 29: char widths[132]; /* widths of characters */ ! 30: int word; /* index of first letter of word */ ! 31: #ifndef NO_DIGRAPH ! 32: int erased; /* 0, or first char of a digraph */ ! 33: #endif ! 34: ! 35: /* show the prompt */ ! 36: move(LINES - 1, 0); ! 37: tab = 0; ! 38: if (prompt) ! 39: { ! 40: addch(prompt); ! 41: tab = 1; ! 42: } ! 43: clrtoeol(); ! 44: refresh(); ! 45: ! 46: /* read in the line */ ! 47: #ifndef NO_DIGRAPH ! 48: erased = ! 49: #endif ! 50: quoted = len = 0; ! 51: for (;;) ! 52: { ! 53: #ifndef NO_ABBR ! 54: if (quoted || mode == MODE_EX) ! 55: { ! 56: ch = getkey(0); ! 57: } ! 58: else ! 59: { ! 60: /* maybe expand an abbreviation while getting key */ ! 61: for (word = len; --word >= 0 && isalnum(buf[word]); ) ! 62: { ! 63: } ! 64: word++; ! 65: ch = getabkey(WHEN_EX, &buf[word], len - word); ! 66: } ! 67: #else ! 68: ch = getkey(0); ! 69: #endif ! 70: #ifndef NO_EXTENSIONS ! 71: if (ch == ctrl('O')) ! 72: { ! 73: ch = getkey(quoted ? 0 : WHEN_EX); ! 74: } ! 75: #endif ! 76: ! 77: /* some special conversions */ ! 78: if (ch == ctrl('D') && len == 0) ! 79: ch = ctrl('['); ! 80: #ifndef NO_DIGRAPH ! 81: if (*o_digraph && erased != 0 && ch != '\b') ! 82: { ! 83: ch = digraph(erased, ch); ! 84: erased = 0; ! 85: } ! 86: #endif ! 87: ! 88: /* inhibit detection of special chars (except ^J) after a ^V */ ! 89: if (quoted && ch != '\n') ! 90: { ! 91: ch |= 256; ! 92: } ! 93: ! 94: /* process the character */ ! 95: switch(ch) ! 96: { ! 97: case ctrl('V'): ! 98: qaddch('^'); ! 99: qaddch('\b'); ! 100: quoted = TRUE; ! 101: break; ! 102: ! 103: case ctrl('['): ! 104: return -1; ! 105: ! 106: case '\n': ! 107: #if OSK ! 108: case '\l': ! 109: #else ! 110: case '\r': ! 111: #endif ! 112: clrtoeol(); ! 113: goto BreakBreak; ! 114: ! 115: case '\b': ! 116: if (len > 0) ! 117: { ! 118: len--; ! 119: #ifndef NO_DIGRAPH ! 120: erased = buf[len]; ! 121: #endif ! 122: for (ch = widths[len]; ch > 0; ch--) ! 123: addch('\b'); ! 124: if (mode == MODE_EX) ! 125: { ! 126: clrtoeol(); ! 127: } ! 128: tab -= widths[len]; ! 129: } ! 130: else ! 131: { ! 132: return -1; ! 133: } ! 134: break; ! 135: ! 136: default: ! 137: /* strip off quotation bit */ ! 138: if (ch & 256) ! 139: { ! 140: ch &= ~256; ! 141: qaddch(' '); ! 142: qaddch('\b'); ! 143: } ! 144: ! 145: /* add & echo the char */ ! 146: if (len < bsize - 1) ! 147: { ! 148: if (ch == '\t' && !quoted) ! 149: { ! 150: widths[len] = *o_tabstop - (tab % *o_tabstop); ! 151: addstr(" " + 8 - widths[len]); ! 152: tab += widths[len]; ! 153: } ! 154: else if (ch > 0 && ch < ' ') /* > 0 by GB */ ! 155: { ! 156: addch('^'); ! 157: addch(ch + '@'); ! 158: widths[len] = 2; ! 159: tab += 2; ! 160: } ! 161: else if (ch == '\177') ! 162: { ! 163: addch('^'); ! 164: addch('?'); ! 165: widths[len] = 2; ! 166: tab += 2; ! 167: } ! 168: else ! 169: { ! 170: addch(ch); ! 171: widths[len] = 1; ! 172: tab++; ! 173: } ! 174: buf[len++] = ch; ! 175: } ! 176: else ! 177: { ! 178: beep(); ! 179: } ! 180: quoted = FALSE; ! 181: } ! 182: } ! 183: BreakBreak: ! 184: refresh(); ! 185: buf[len] = '\0'; ! 186: return len; ! 187: } ! 188: ! 189: ! 190: static int manymsgs; /* This variable keeps msgs from overwriting each other */ ! 191: static char pmsg[80]; /* previous message (waiting to be displayed) */ ! 192: ! 193: ! 194: static int showmsg() ! 195: { ! 196: /* if there is no message to show, then don't */ ! 197: if (!manymsgs) ! 198: return FALSE; ! 199: ! 200: /* display the message */ ! 201: move(LINES - 1, 0); ! 202: if (*pmsg) ! 203: { ! 204: standout(); ! 205: qaddch(' '); ! 206: qaddstr(pmsg); ! 207: qaddch(' '); ! 208: standend(); ! 209: } ! 210: clrtoeol(); ! 211: ! 212: manymsgs = FALSE; ! 213: return TRUE; ! 214: } ! 215: ! 216: ! 217: void endmsgs() ! 218: { ! 219: if (manymsgs) ! 220: { ! 221: showmsg(); ! 222: addch('\n'); ! 223: } ! 224: } ! 225: ! 226: /* Write a message in an appropriate way. This should really be a varargs ! 227: * function, but there is no such thing as vwprintw. Hack!!! ! 228: * ! 229: * In MODE_EX or MODE_COLON, the message is written immediately, with a ! 230: * newline at the end. ! 231: * ! 232: * In MODE_VI, the message is stored in a character buffer. It is not ! 233: * displayed until getkey() is called. msg() will call getkey() itself, ! 234: * if necessary, to prevent messages from being lost. ! 235: * ! 236: * msg("") - clears the message line ! 237: * msg("%s %d", ...) - does a printf onto the message line ! 238: */ ! 239: #ifdef __STDC__ ! 240: void msg (char *fmt, ...) ! 241: { ! 242: va_list ap; ! 243: va_start (ap, fmt); ! 244: #else ! 245: void msg(fmt, arg1, arg2, arg3, arg4, arg5, arg6, arg7) ! 246: char *fmt; ! 247: long arg1, arg2, arg3, arg4, arg5, arg6, arg7; ! 248: { ! 249: #endif ! 250: if (mode != MODE_VI) ! 251: { ! 252: #ifdef __STDC__ ! 253: vsprintf (pmsg, fmt, ap); ! 254: #else ! 255: sprintf(pmsg, fmt, arg1, arg2, arg3, arg4, arg5, arg6, arg7); ! 256: #endif ! 257: qaddstr(pmsg); ! 258: addch('\n'); ! 259: exrefresh(); ! 260: } ! 261: else ! 262: { ! 263: /* wait for keypress between consecutive msgs */ ! 264: if (manymsgs) ! 265: { ! 266: getkey(WHEN_MSG); ! 267: } ! 268: ! 269: /* real message */ ! 270: #ifdef __STDC__ ! 271: vsprintf (pmsg, fmt, ap); ! 272: #else ! 273: sprintf(pmsg, fmt, arg1, arg2, arg3, arg4, arg5, arg6, arg7); ! 274: #endif ! 275: if (*fmt) ! 276: { ! 277: manymsgs = TRUE; ! 278: } ! 279: } ! 280: #ifdef __STDC__ ! 281: va_end (ap); ! 282: #endif ! 283: } ! 284: ! 285: ! 286: /* This function calls refresh() if the option exrefresh is set */ ! 287: void exrefresh() ! 288: { ! 289: char *scan; ! 290: ! 291: /* If this ex command wrote ANYTHING set exwrote so vi's : command ! 292: * can tell that it must wait for a user keystroke before redrawing. ! 293: */ ! 294: for (scan=kbuf; scan<stdscr; scan++) ! 295: if (*scan == '\n') ! 296: exwrote = TRUE; ! 297: ! 298: /* now we do the refresh thing */ ! 299: if (*o_exrefresh) ! 300: { ! 301: refresh(); ! 302: } ! 303: else ! 304: { ! 305: wqrefresh(); ! 306: } ! 307: if (mode != MODE_VI) ! 308: { ! 309: manymsgs = FALSE; ! 310: } ! 311: } ! 312: ! 313: ! 314: /* This structure is used to store maps and abbreviations. The distinction ! 315: * between them is that maps are stored in the list referenced by the "maps" ! 316: * pointer, while abbreviations are referenced by the "abbrs" pointer. ! 317: */ ! 318: typedef struct _map ! 319: { ! 320: struct _map *next; /* another abbreviation */ ! 321: short len; /* length of the "rawin" characters */ ! 322: short flags; /* various flags */ ! 323: char *label; /* label of the map/abbr, or NULL */ ! 324: char *rawin; /* the "rawin" characters */ ! 325: char *cooked;/* the "cooked" characters */ ! 326: } MAP; ! 327: ! 328: static char keybuf[KEYBUFSIZE]; ! 329: static int cend; /* end of input characters */ ! 330: static int user; /* from user through end are chars typed by user */ ! 331: static int next; /* index of the next character to be returned */ ! 332: static MAP *match; /* the matching map, found by countmatch() */ ! 333: static MAP *maps; /* the map table */ ! 334: #ifndef NO_ABBR ! 335: static MAP *abbrs; /* the abbreviation table */ ! 336: #endif ! 337: ! 338: ! 339: ! 340: /* ring the terminal's bell */ ! 341: void beep() ! 342: { ! 343: /* do a visible/audible bell */ ! 344: if (*o_flash) ! 345: { ! 346: do_VB(); ! 347: refresh(); ! 348: } ! 349: else if (*o_errorbells) ! 350: { ! 351: tputs("\007", 1, faddch); ! 352: } ! 353: ! 354: /* discard any buffered input, and abort macros */ ! 355: next = user = cend; ! 356: } ! 357: ! 358: ! 359: ! 360: /* This function replaces a "rawin" character sequence with the "cooked" version, ! 361: * by modifying the internal type-ahead buffer. ! 362: */ ! 363: ! 364: void execmap(rawlen, cookedstr, visual) ! 365: int rawlen; /* length of rawin text -- string to delete */ ! 366: char *cookedstr; /* the cooked text -- string to insert */ ! 367: int visual; /* boolean -- chars to be executed in visual mode? */ ! 368: { ! 369: int cookedlen; ! 370: char *src, *dst; ! 371: int i; ! 372: ! 373: /* find the length of the cooked string */ ! 374: cookedlen = strlen(cookedstr); ! 375: #ifndef NO_EXTENSIONS ! 376: if (visual) ! 377: { ! 378: cookedlen *= 2; ! 379: } ! 380: #endif ! 381: ! 382: /* if too big to fit in type-ahead buffer, then don't do it */ ! 383: if (cookedlen + (cend - next) - rawlen > KEYBUFSIZE) ! 384: { ! 385: return; ! 386: } ! 387: ! 388: /* shift to make room for cookedstr at the front of keybuf */ ! 389: src = &keybuf[next + rawlen]; ! 390: dst = &keybuf[cookedlen]; ! 391: i = cend - (next + rawlen); ! 392: if (src >= dst) ! 393: { ! 394: while (i-- > 0) ! 395: { ! 396: *dst++ = *src++; ! 397: } ! 398: } ! 399: else ! 400: { ! 401: src += i; ! 402: dst += i; ! 403: while (i-- > 0) ! 404: { ! 405: *--dst = *--src; ! 406: } ! 407: } ! 408: ! 409: /* insert cookedstr, and adjust offsets */ ! 410: cend += cookedlen - rawlen - next; ! 411: user += cookedlen - rawlen - next; ! 412: next = 0; ! 413: for (dst = keybuf, src = cookedstr; *src; ) ! 414: { ! 415: #ifndef NO_EXTENSIONS ! 416: if (visual) ! 417: { ! 418: *dst++ = ctrl('O'); ! 419: cookedlen--; ! 420: } ! 421: #endif ! 422: *dst++ = *src++; ! 423: } ! 424: ! 425: #ifdef DEBUG2 ! 426: { ! 427: #include <stdio.h> ! 428: FILE *debout; ! 429: int i; ! 430: ! 431: debout = fopen("debug.out", "a"); ! 432: fprintf(debout, "After execmap(%d, \"%s\", %d)...\n", rawlen, cookedstr, visual); ! 433: for (i = 0; i < cend; i++) ! 434: { ! 435: if (i == next) fprintf(debout, "(next)"); ! 436: if (i == user) fprintf(debout, "(user)"); ! 437: if (UCHAR(keybuf[i]) < ' ') ! 438: fprintf(debout, "^%c", keybuf[i] ^ '@'); ! 439: else ! 440: fprintf(debout, "%c", keybuf[i]); ! 441: } ! 442: fprintf(debout, "(end)\n"); ! 443: fclose(debout); ! 444: } ! 445: #endif ! 446: } ! 447: ! 448: /* This function calls ttyread(). If necessary, it will also redraw the screen, ! 449: * change the cursor shape, display the mode, and update the ruler. If the ! 450: * number of characters read is 0, and we didn't time-out, then it exits because ! 451: * we've apparently reached the end of an EX script. ! 452: */ ! 453: static int fillkeybuf(when, timeout) ! 454: int when; /* mixture of WHEN_XXX flags */ ! 455: int timeout;/* timeout in 1/10 second increments, or 0 */ ! 456: { ! 457: int nkeys; ! 458: #ifndef NO_SHOWMODE ! 459: static int oldwhen; /* "when" from last time */ ! 460: static int oldleft; ! 461: static long oldtop; ! 462: static long oldnlines; ! 463: char *str; ! 464: #endif ! 465: #ifndef NO_CURSORSHAPE ! 466: static int oldcurs; ! 467: #endif ! 468: ! 469: #ifdef DEBUG ! 470: watch(); ! 471: #endif ! 472: ! 473: ! 474: #ifndef NO_CURSORSHAPE ! 475: /* make sure the cursor is the right shape */ ! 476: if (has_CQ) ! 477: { ! 478: if (when != oldcurs) ! 479: { ! 480: switch (when) ! 481: { ! 482: case WHEN_EX: do_CX(); break; ! 483: case WHEN_VICMD: do_CV(); break; ! 484: case WHEN_VIINP: do_CI(); break; ! 485: case WHEN_VIREP: do_CR(); break; ! 486: } ! 487: oldcurs = when; ! 488: } ! 489: } ! 490: #endif ! 491: ! 492: #ifndef NO_SHOWMODE ! 493: /* if "showmode" then say which mode we're in */ ! 494: if (*o_smd && (when & WHENMASK)) ! 495: { ! 496: /* redraw the screen before we check to see whether the ! 497: * "showmode" message needs to be redrawn. ! 498: */ ! 499: redraw(cursor, !(when & WHEN_VICMD)); ! 500: ! 501: /* now the "topline" test should be valid */ ! 502: if (when != oldwhen || topline != oldtop || leftcol != oldleft || nlines != oldnlines) ! 503: { ! 504: oldwhen = when; ! 505: oldtop = topline; ! 506: oldleft = leftcol; ! 507: oldnlines = nlines; ! 508: ! 509: if (when & WHEN_VICMD) str = "Command"; ! 510: else if (when & WHEN_VIINP) str = " Input "; ! 511: else if (when & WHEN_VIREP) str = "Replace"; ! 512: else if (when & WHEN_REP1) str = " Rep 1 "; ! 513: else if (when & WHEN_CUT) str = "BufName"; ! 514: else if (when & WHEN_MARK) str = "Mark AZ"; ! 515: else if (when & WHEN_CHAR) str = "Dest Ch"; ! 516: else str = (char *)0; ! 517: ! 518: if (str) ! 519: { ! 520: move(LINES - 1, COLS - 10); ! 521: standout(); ! 522: qaddstr(str); ! 523: standend(); ! 524: } ! 525: } ! 526: } ! 527: #endif ! 528: ! 529: #ifndef NO_EXTENSIONS ! 530: /* maybe display the ruler */ ! 531: if (*o_ruler && (when & (WHEN_VICMD|WHEN_VIINP|WHEN_VIREP))) ! 532: { ! 533: char buf[20]; ! 534: ! 535: redraw(cursor, !(when & WHEN_VICMD)); ! 536: pfetch(markline(cursor)); ! 537: sprintf(buf, "%7ld,%-4d", markline(cursor), 1 + idx2col(cursor, ptext, when & (WHEN_VIINP|WHEN_VIREP))); ! 538: move(LINES - 1, COLS - 22); ! 539: addstr(buf); ! 540: } ! 541: #endif ! 542: ! 543: /* redraw, so the cursor is in the right place */ ! 544: if (when & WHENMASK) ! 545: { ! 546: redraw(cursor, !(when & (WHENMASK & ~(WHEN_VIREP|WHEN_VIINP)))); ! 547: } ! 548: ! 549: /* Okay, now we can finally read the rawin keystrokes */ ! 550: refresh(); ! 551: nkeys = ttyread(keybuf + cend, sizeof keybuf - cend, timeout); ! 552: ! 553: /* if nkeys == 0 then we've reached EOF of an ex script. */ ! 554: if (nkeys == 0 && timeout == 0) ! 555: { ! 556: tmpabort(TRUE); ! 557: move(LINES - 1, 0); ! 558: clrtoeol(); ! 559: refresh(); ! 560: endwin(); ! 561: exit(exitcode); ! 562: } ! 563: ! 564: cend += nkeys; ! 565: user += nkeys; ! 566: return nkeys; ! 567: } ! 568: ! 569: ! 570: /* This function counts the number of maps that could match the characters ! 571: * between &keybuf[next] and &keybuf[cend], including incomplete matches. ! 572: * The longest comlete match is remembered via the "match" variable. ! 573: */ ! 574: static int countmatch(when) ! 575: int when; /* mixture of WHEN_XXX flags */ ! 576: { ! 577: MAP *map; ! 578: int count; ! 579: ! 580: /* clear the "match" variable */ ! 581: match = (MAP *)0; ! 582: ! 583: /* check every map */ ! 584: for (count = 0, map = maps; map; map = map->next) ! 585: { ! 586: /* can't match if wrong mode */ ! 587: if ((map->flags & when) == 0) ! 588: { ! 589: continue; ! 590: } ! 591: ! 592: /* would this be a complete match? */ ! 593: if (map->len <= cend - next) ! 594: { ! 595: /* Yes, it would be. Now does it really match? */ ! 596: if (!strncmp(map->rawin, &keybuf[next], map->len)) ! 597: { ! 598: count++; ! 599: ! 600: /* if this is the longest complete match, ! 601: * then remember it. ! 602: */ ! 603: if (!match || match->len < map->len) ! 604: { ! 605: match = map; ! 606: } ! 607: } ! 608: } ! 609: else ! 610: { ! 611: /* No, it wouldn't. But check for partial match */ ! 612: if (!strncmp(map->rawin, &keybuf[next], cend - next)) ! 613: { ! 614: count++; ! 615: } ! 616: } ! 617: } ! 618: return count; ! 619: } ! 620: ! 621: ! 622: #ifndef NO_ABBR ! 623: /* This function checks to see whether a word is an abbreviation. If it is, ! 624: * then an appropriate number of backspoace characters is inserted into the ! 625: * type-ahead buffer, followed by the expanded form of the abbreviation. ! 626: */ ! 627: static void expandabbr(word, wlen) ! 628: char *word; ! 629: int wlen; ! 630: { ! 631: MAP *abbr; ! 632: ! 633: /* if the next character wouldn't end the word, then don't expand */ ! 634: if (isalnum(keybuf[next]) || keybuf[next] == ctrl('V')) ! 635: { ! 636: return; ! 637: } ! 638: ! 639: /* find the abbreviation, if any */ ! 640: for (abbr = abbrs; ! 641: abbr && (abbr->len != wlen || strncmp(abbr->rawin, word, wlen)); ! 642: abbr = abbr->next) ! 643: { ! 644: } ! 645: ! 646: /* If an abbreviation was found, then expand it by inserting the long ! 647: * version into the type-ahead buffer, and then inserting (in front of ! 648: * the long version) enough backspaces to erase to the short version. ! 649: */ ! 650: if (abbr) ! 651: { ! 652: execmap(0, abbr->cooked, FALSE); ! 653: while (wlen > 15) ! 654: { ! 655: execmap(0, "\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b", FALSE); ! 656: wlen -= 15; ! 657: } ! 658: if (wlen > 0) ! 659: { ! 660: execmap(0, "\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b" + 15 - wlen, FALSE); ! 661: } ! 662: } ! 663: } ! 664: #endif ! 665: ! 666: ! 667: /* This function calls getabkey() without attempting to expand abbreviations */ ! 668: int getkey(when) ! 669: int when; /* mixture of WHEN_XXX flags */ ! 670: { ! 671: return getabkey(when, "", 0); ! 672: } ! 673: ! 674: ! 675: /* This is it. This function returns keystrokes one-at-a-time, after mapping ! 676: * and abbreviations have been taken into account. ! 677: */ ! 678: int getabkey(when, word, wlen) ! 679: int when; /* mixture of WHEN_XXX flags */ ! 680: char *word; /* a word that may need to be expanded as an abbr */ ! 681: int wlen; /* length of "word" -- since "word" might not have \0 */ ! 682: { ! 683: int matches; ! 684: ! 685: /* if this key is needed for delay between multiple error messages, ! 686: * then reset the manymsgs flag and abort any mapped key sequence. ! 687: */ ! 688: if (showmsg()) ! 689: { ! 690: if (when == WHEN_MSG) ! 691: { ! 692: #ifndef CRUNCH ! 693: if (!*o_more) ! 694: { ! 695: refresh(); ! 696: return ' '; ! 697: } ! 698: #endif ! 699: qaddstr("[More...]"); ! 700: refresh(); ! 701: execmap(user, "", FALSE); ! 702: } ! 703: } ! 704: ! 705: #ifdef DEBUG ! 706: /* periodically check for screwed up internal tables */ ! 707: watch(); ! 708: #endif ! 709: ! 710: /* if buffer empty, read some characters without timeout */ ! 711: if (next >= cend) ! 712: { ! 713: next = user = cend = 0; ! 714: fillkeybuf(when, 0); ! 715: } ! 716: ! 717: /* try to map the key, unless already mapped and not ":set noremap" */ ! 718: if (next >= user || *o_remap) ! 719: { ! 720: do ! 721: { ! 722: do ! 723: { ! 724: matches = countmatch(when); ! 725: } while (matches > 1 && fillkeybuf(when, *o_keytime) > 0); ! 726: if (matches == 1) ! 727: { ! 728: if (match) { ! 729: execmap(match->len, match->cooked, ! 730: (match->flags & WHEN_INMV) != 0 ! 731: && (when & (WHEN_VIINP|WHEN_VIREP)) != 0); ! 732: } else ! 733: matches = 0; ! 734: } ! 735: } while (*o_remap && matches == 1); ! 736: } ! 737: ! 738: #ifndef NO_ABBR ! 739: /* try to expand an abbreviation, except in visual command mode */ ! 740: if (wlen > 0 && (mode & (WHEN_EX|WHEN_VIINP|WHEN_VIREP)) != 0) ! 741: { ! 742: expandabbr(word, wlen); ! 743: } ! 744: #endif ! 745: ! 746: /* ERASEKEY should always be mapped to '\b'. */ ! 747: if (keybuf[next] == ERASEKEY) ! 748: { ! 749: keybuf[next] = '\b'; ! 750: } ! 751: ! 752: /* return the next key */ ! 753: return keybuf[next++]; ! 754: } ! 755: ! 756: /* This function maps or unmaps a key */ ! 757: void mapkey(rawin, cooked, when, name) ! 758: char *rawin; /* the input key sequence, before mapping */ ! 759: char *cooked;/* after mapping -- or NULL to remove map */ ! 760: int when; /* bitmap of when mapping should happen */ ! 761: char *name; /* name of the key, NULL for no name, "abbr" for abbr */ ! 762: { ! 763: MAP **head; /* head of list of maps or abbreviations */ ! 764: MAP *scan; /* used for scanning through the list */ ! 765: MAP *prev; /* used during deletions */ ! 766: ! 767: /* Is this a map or an abbreviation? Choose the right list. */ ! 768: #ifndef NO_ABBR ! 769: head = ((!name || strcmp(name, "abbr")) ? &maps : &abbrs); ! 770: #else ! 771: head = &maps; ! 772: #endif ! 773: ! 774: /* try to find the map in the list */ ! 775: for (scan = *head, prev = (MAP *)0; ! 776: scan && (strcmp(rawin, scan->rawin) || ! 777: !(scan->flags & when & (WHEN_EX|WHEN_VICMD|WHEN_VIINP|WHEN_VIREP))); ! 778: prev = scan, scan = scan->next) ! 779: { ! 780: } ! 781: ! 782: /* trying to map? (not unmap) */ ! 783: if (cooked && *cooked) ! 784: { ! 785: /* if map starts with "visual ", then mark it as a visual map */ ! 786: if (head == &maps && !strncmp(cooked, "visual ", 7)) ! 787: { ! 788: cooked += 7; ! 789: when |= WHEN_INMV; ! 790: } ! 791: ! 792: /* "visual" maps always work in input mode */ ! 793: if (when & WHEN_INMV) ! 794: { ! 795: when |= WHEN_VIINP|WHEN_VIREP|WHEN_POPUP; ! 796: } ! 797: ! 798: /* if not already in the list, then allocate a new structure */ ! 799: if (!scan) ! 800: { ! 801: scan = (MAP *)malloc(sizeof(MAP)); ! 802: scan->len = strlen(rawin); ! 803: scan->rawin = malloc((unsigned)(scan->len + 1)); ! 804: strcpy(scan->rawin, rawin); ! 805: scan->flags = when; ! 806: scan->label = name; ! 807: if (*head) ! 808: { ! 809: prev->next = scan; ! 810: } ! 811: else ! 812: { ! 813: *head = scan; ! 814: } ! 815: scan->next = (MAP *)0; ! 816: } ! 817: else /* recycle old structure */ ! 818: { ! 819: _free_(scan->cooked); ! 820: } ! 821: scan->cooked = malloc((unsigned)(strlen(cooked) + 1)); ! 822: strcpy(scan->cooked, cooked); ! 823: } ! 824: else /* unmapping */ ! 825: { ! 826: /* if nothing to unmap, then exit silently */ ! 827: if (!scan) ! 828: { ! 829: return; ! 830: } ! 831: ! 832: /* unlink the structure from the list */ ! 833: if (prev) ! 834: { ! 835: prev->next = scan->next; ! 836: } ! 837: else ! 838: { ! 839: *head = scan->next; ! 840: } ! 841: ! 842: /* free it, and the strings that it refers to */ ! 843: _free_(scan->rawin); ! 844: _free_(scan->cooked); ! 845: _free_(scan); ! 846: } ! 847: } ! 848: ! 849: ! 850: /* This function returns a printable version of a string. It uses tmpblk.c */ ! 851: char *printable(str) ! 852: char *str; /* the string to convert */ ! 853: { ! 854: char *build; /* used for building the string */ ! 855: ! 856: for (build = tmpblk.c; *str; str++) ! 857: { ! 858: #if AMIGA ! 859: if (*str == '\233') ! 860: { ! 861: *build++ = '<'; ! 862: *build++ = 'C'; ! 863: *build++ = 'S'; ! 864: *build++ = 'I'; ! 865: *build++ = '>'; ! 866: } else ! 867: #endif ! 868: if (UCHAR(*str) < ' ' || *str == '\177') ! 869: { ! 870: *build++ = '^'; ! 871: *build++ = *str ^ '@'; ! 872: } ! 873: else ! 874: { ! 875: *build++ = *str; ! 876: } ! 877: } ! 878: *build = '\0'; ! 879: return tmpblk.c; ! 880: } ! 881: ! 882: /* This function displays the contents of either the map table or the ! 883: * abbreviation table. User commands call this function as follows: ! 884: * :map dumpkey(WHEN_VICMD, FALSE); ! 885: * :map! dumpkey(WHEN_VIREP|WHEN_VIINP, FALSE); ! 886: * :abbr dumpkey(WHEN_VIINP|WHEN_VIREP, TRUE); ! 887: * :abbr! dumpkey(WHEN_EX|WHEN_VIINP|WHEN_VIREP, TRUE); ! 888: */ ! 889: void dumpkey(when, abbr) ! 890: int when; /* WHEN_XXXX of mappings to be dumped */ ! 891: int abbr; /* boolean: dump abbreviations instead of maps? */ ! 892: { ! 893: MAP *scan; ! 894: char *str; ! 895: int len; ! 896: ! 897: #ifndef NO_ABBR ! 898: for (scan = (abbr ? abbrs : maps); scan; scan = scan->next) ! 899: #else ! 900: for (scan = maps; scan; scan = scan->next) ! 901: #endif ! 902: { ! 903: /* skip entries that don't match "when" */ ! 904: if ((scan->flags & when) == 0) ! 905: { ! 906: continue; ! 907: } ! 908: ! 909: /* dump the key label, if any */ ! 910: if (!abbr) ! 911: { ! 912: len = 8; ! 913: if (scan->label) ! 914: { ! 915: qaddstr(scan->label); ! 916: len -= strlen(scan->label); ! 917: } ! 918: do ! 919: { ! 920: qaddch(' '); ! 921: } while (len-- > 0); ! 922: } ! 923: ! 924: /* dump the rawin version */ ! 925: str = printable(scan->rawin); ! 926: qaddstr(str); ! 927: len = strlen(str); ! 928: do ! 929: { ! 930: qaddch(' '); ! 931: } while (len++ < 8); ! 932: ! 933: /* dump the mapped version */ ! 934: #ifndef NO_EXTENSIONS ! 935: if ((scan->flags & WHEN_INMV) && (when & (WHEN_VIINP|WHEN_VIREP))) ! 936: { ! 937: qaddstr("visual "); ! 938: } ! 939: #endif ! 940: str = printable(scan->cooked); ! 941: qaddstr(str); ! 942: addch('\n'); ! 943: exrefresh(); ! 944: } ! 945: } ! 946: ! 947: #ifndef NO_MKEXRC ! 948: ! 949: static void safequote(str) ! 950: char *str; ! 951: { ! 952: char *build; ! 953: ! 954: build = tmpblk.c + strlen(tmpblk.c); ! 955: while (*str) ! 956: { ! 957: if (*str <= ' ' && *str >= 1 || *str == '|') ! 958: { ! 959: *build++ = ctrl('V'); ! 960: } ! 961: *build++ = *str++; ! 962: } ! 963: *build = '\0'; ! 964: } ! 965: ! 966: /* This function saves the contents of either the map table or the ! 967: * abbreviation table into a file. Both the "bang" and "no bang" versions ! 968: * are saved. ! 969: * :map dumpkey(WHEN_VICMD, FALSE); ! 970: * :map! dumpkey(WHEN_VIREP|WHEN_VIINP, FALSE); ! 971: * :abbr dumpkey(WHEN_VIINP|WHEN_VIREP, TRUE); ! 972: * :abbr! dumpkey(WHEN_EX|WHEN_VIINP|WHEN_VIREP, TRUE); ! 973: */ ! 974: void ! 975: savemaps(fd, abbr) ! 976: int fd; /* file descriptor of an open file to write to */ ! 977: int abbr; /* boolean: do abbr table? (else do map table) */ ! 978: { ! 979: MAP *scan; ! 980: int bang; ! 981: int when; ! 982: ! 983: # ifndef NO_ABBR ! 984: for (scan = (abbr ? abbrs : maps); scan; scan = scan->next) ! 985: # else ! 986: for (scan = maps; scan; scan = scan->next) ! 987: # endif ! 988: { ! 989: /* skip maps that have labels, except for function keys */ ! 990: if (scan->label && *scan->label != '#') ! 991: { ! 992: continue; ! 993: } ! 994: ! 995: for (bang = 0; bang < 2; bang++) ! 996: { ! 997: /* decide which "when" flags we want */ ! 998: # ifndef NO_ABBR ! 999: if (abbr) ! 1000: when = (bang ? WHEN_EX|WHEN_VIINP|WHEN_VIREP : WHEN_VIINP|WHEN_VIREP); ! 1001: else ! 1002: # endif ! 1003: when = (bang ? WHEN_VIREP|WHEN_VIINP : WHEN_VICMD); ! 1004: ! 1005: /* skip entries that don't match "when" */ ! 1006: if ((scan->flags & when) == 0) ! 1007: { ! 1008: continue; ! 1009: } ! 1010: ! 1011: /* write a "map" or "abbr" command name */ ! 1012: # ifndef NO_ABBR ! 1013: if (abbr) ! 1014: strcpy(tmpblk.c, "abbr"); ! 1015: else ! 1016: # endif ! 1017: strcpy(tmpblk.c, "map"); ! 1018: ! 1019: /* maybe write a bang. Definitely write a space */ ! 1020: if (bang) ! 1021: strcat(tmpblk.c, "! "); ! 1022: else ! 1023: strcat(tmpblk.c, " "); ! 1024: ! 1025: /* write the rawin version */ ! 1026: # ifndef NO_FKEY ! 1027: if (scan->label) ! 1028: strcat(tmpblk.c, scan->label); ! 1029: else ! 1030: # endif ! 1031: safequote(scan->rawin); ! 1032: strcat(tmpblk.c, " "); ! 1033: ! 1034: /* dump the mapped version */ ! 1035: # ifndef NO_EXTENSIONS ! 1036: if ((scan->flags & WHEN_INMV) && (when & (WHEN_VIINP|WHEN_VIREP))) ! 1037: { ! 1038: strcat(tmpblk.c, "visual "); ! 1039: } ! 1040: # endif ! 1041: safequote(scan->cooked); ! 1042: strcat(tmpblk.c, "\n"); ! 1043: twrite(fd, tmpblk.c, strlen(tmpblk.c)); ! 1044: } ! 1045: } ! 1046: } ! 1047: #endif
This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.