home *** CD-ROM | disk | FTP | other *** search
/ OS/2 Shareware BBS: 5 Edit / 05-Edit.zip / elvis184.zip / src / tio.c < prev    next >
C/C++ Source or Header  |  1995-05-26  |  27KB  |  1,263 lines

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