home *** CD-ROM | disk | FTP | other *** search
/ Crawly Crypt Collection 2 / crawlyvol2.bin / apps / text_ed / elv16b2 / st / tio.c < prev    next >
C/C++ Source or Header  |  1992-05-13  |  22KB  |  1,045 lines

  1. /* tio.c */
  2.  
  3. /* Author:
  4.  *    Steve Kirkendall
  5.  *    14407 SW Teal Blvd. #C
  6.  *    Beaverton, OR 97005
  7.  *    kirkenda@cs.pdx.edu
  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. void execmap(rawlen, cookedstr, visual)
  364.     int    rawlen;        /* length of rawin text -- string to delete */
  365.     char    *cookedstr;    /* the cooked text -- string to insert */
  366.     int    visual;        /* boolean -- chars to be executed in visual mode? */
  367. {
  368.     int    cookedlen;
  369.     char    *src, *dst;
  370.     int    i;
  371.  
  372.     /* find the length of the cooked string */
  373.     cookedlen = strlen(cookedstr);
  374. #ifndef NO_EXTENSIONS
  375.     if (visual)
  376.     {
  377.         cookedlen *= 2;
  378.     }
  379. #endif
  380.  
  381.     /* if too big to fit in type-ahead buffer, then don't do it */
  382.     if (cookedlen + (cend - next) - rawlen > KEYBUFSIZE)
  383.     {
  384.         return;
  385.     }
  386.  
  387.     /* shift to make room for cookedstr at the front of keybuf */
  388.     src = &keybuf[next + rawlen];
  389.     dst = &keybuf[cookedlen];
  390.     i = cend - (next + rawlen);
  391.     if (src >= dst)
  392.     {
  393.         while (i-- > 0)
  394.         {
  395.             *dst++ = *src++;
  396.         }
  397.     }
  398.     else
  399.     {
  400.         src += i;
  401.         dst += i;
  402.         while (i-- > 0)
  403.         {
  404.             *--dst = *--src;
  405.         }
  406.     }
  407.  
  408.     /* insert cookedstr, and adjust offsets */
  409.     cend += cookedlen - rawlen - next;
  410.     user += cookedlen - rawlen - next;
  411.     next = 0;
  412.     for (dst = keybuf, src = cookedstr; *src; )
  413.     {
  414. #ifndef NO_EXTENSIONS
  415.         if (visual)
  416.         {
  417.             *dst++ = ctrl('O');
  418.             cookedlen--;
  419.         }
  420. #endif
  421.         *dst++ = *src++;
  422.     }
  423.  
  424. #ifdef DEBUG2
  425.     {
  426. #include <stdio.h>
  427.         FILE    *debout;
  428.         int        i;
  429.  
  430.         debout = fopen("debug.out", "a");
  431.         fprintf(debout, "After execmap(%d, \"%s\", %d)...\n", rawlen, cookedstr, visual);
  432.         for (i = 0; i < cend; i++)
  433.         {
  434.             if (i == next) fprintf(debout, "(next)");
  435.             if (i == user) fprintf(debout, "(user)");
  436.             if (UCHAR(keybuf[i]) < ' ')
  437.                 fprintf(debout, "^%c", keybuf[i] ^ '@');
  438.             else
  439.                 fprintf(debout, "%c", keybuf[i]);
  440.         }
  441.         fprintf(debout, "(end)\n");
  442.         fclose(debout);
  443.     }
  444. #endif
  445. }
  446.  
  447. /* This function calls ttyread().  If necessary, it will also redraw the screen,
  448.  * change the cursor shape, display the mode, and update the ruler.  If the
  449.  * number of characters read is 0, and we didn't time-out, then it exits because
  450.  * we've apparently reached the end of an EX script.
  451.  */
  452. #ifndef NO_CURSORSHAPE
  453. /* made global so that suspend_curses() can reset it  -nox */
  454. int    oldcurs;
  455. #endif
  456. static int fillkeybuf(when, timeout)
  457.     int    when;    /* mixture of WHEN_XXX flags */
  458.     int    timeout;/* timeout in 1/10 second increments, or 0 */
  459. {
  460.     int    nkeys;
  461. #ifndef NO_SHOWMODE
  462.     static int    oldwhen;    /* "when" from last time */
  463.     static int    oldleft;
  464.     static long    oldtop;
  465.     static long    oldnlines;
  466.     char        *str;
  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, (int) 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.                 execmap(match->len, match->cooked,
  729.                     (match->flags & WHEN_INMV) != 0 
  730.                      && (when & (WHEN_VIINP|WHEN_VIREP)) != 0);
  731.             }
  732.         } while (*o_remap && matches == 1);
  733.     }
  734.  
  735. #ifndef NO_ABBR
  736.     /* try to expand an abbreviation, except in visual command mode */
  737.     if (wlen > 0 && (mode & (WHEN_EX|WHEN_VIINP|WHEN_VIREP)) != 0)
  738.     {
  739.         expandabbr(word, wlen);
  740.     }
  741. #endif
  742.  
  743.     /* ERASEKEY should always be mapped to '\b'. */
  744.     if (keybuf[next] == ERASEKEY)
  745.     {
  746.         keybuf[next] = '\b';
  747.     }
  748.  
  749.     /* return the next key */
  750.     return keybuf[next++];
  751. }
  752.  
  753. /* This function maps or unmaps a key */
  754. void mapkey(rawin, cooked, when, name)
  755.     char    *rawin;    /* the input key sequence, before mapping */
  756.     char    *cooked;/* after mapping -- or NULL to remove map */
  757.     int    when;    /* bitmap of when mapping should happen */
  758.     char    *name;    /* name of the key, NULL for no name, "abbr" for abbr */
  759. {
  760.     MAP    **head;    /* head of list of maps or abbreviations */
  761.     MAP    *scan;    /* used for scanning through the list */
  762.     MAP    *prev;    /* used during deletions */
  763.  
  764.     /* Is this a map or an abbreviation?  Choose the right list. */
  765. #ifndef NO_ABBR
  766.     head = ((!name || strcmp(name, "abbr")) ? &maps : &abbrs);
  767. #else
  768.     head = &maps;
  769. #endif
  770.  
  771.     /* try to find the map in the list */
  772.     for (scan = *head, prev = (MAP *)0;
  773.          scan && (strcmp(rawin, scan->rawin) ||
  774.         !(scan->flags & when & (WHEN_EX|WHEN_VICMD|WHEN_VIINP|WHEN_VIREP)));
  775.          prev = scan, scan = scan->next)
  776.     {
  777.     }
  778.  
  779.     /* trying to map? (not unmap) */
  780.     if (cooked && *cooked)
  781.     {
  782.         /* if map starts with "visual ", then mark it as a visual map */
  783.         if (head == &maps && !strncmp(cooked, "visual ", 7))
  784.         {
  785.             cooked += 7;
  786.             when |= WHEN_INMV;
  787.         }
  788.  
  789.         /* "visual" maps always work in input mode */
  790.         if (when & WHEN_INMV)
  791.         {
  792.             when |= WHEN_VIINP|WHEN_VIREP|WHEN_POPUP;
  793.         }
  794.  
  795.         /* if not already in the list, then allocate a new structure */
  796.         if (!scan)
  797.         {
  798.             scan = (MAP *)malloc(sizeof(MAP));
  799.             scan->len = strlen(rawin);
  800.             scan->rawin = malloc((unsigned)(scan->len + 1));
  801.             strcpy(scan->rawin, rawin);
  802.             scan->flags = when;
  803.             scan->label = name;
  804.             if (*head)
  805.             {
  806.                 prev->next = scan;
  807.             }
  808.             else
  809.             {
  810.                 *head = scan;
  811.             }
  812.             scan->next = (MAP *)0;
  813.         }
  814.         else /* recycle old structure */
  815.         {
  816.             _free_(scan->cooked);
  817.         }
  818.         scan->cooked = malloc((unsigned)(strlen(cooked) + 1));
  819.         strcpy(scan->cooked, cooked);
  820.     }
  821.     else /* unmapping */
  822.     {
  823.         /* if nothing to unmap, then exit silently */
  824.         if (!scan)
  825.         {
  826.             return;
  827.         }
  828.  
  829.         /* unlink the structure from the list */
  830.         if (prev)
  831.         {
  832.             prev->next = scan->next;
  833.         }
  834.         else
  835.         {
  836.             *head = scan->next;
  837.         }
  838.  
  839.         /* free it, and the strings that it refers to */
  840.         _free_(scan->rawin);
  841.         _free_(scan->cooked);
  842.         _free_(scan);
  843.     }
  844. }
  845.  
  846.  
  847. /* This function returns a printable version of a string.  It uses tmpblk.c */
  848. char *printable(str)
  849.     char    *str;    /* the string to convert */
  850. {
  851.     char    *build;    /* used for building the string */
  852.  
  853.     for (build = tmpblk.c; *str; str++)
  854.     {
  855. #if AMIGA
  856.         if (*str == '\233')
  857.         {
  858.             *build++ = '<';
  859.             *build++ = 'C';
  860.             *build++ = 'S';
  861.             *build++ = 'I';
  862.             *build++ = '>';
  863.         } else 
  864. #endif
  865.         if (UCHAR(*str) < ' ' || *str == '\177')
  866.         {
  867.             *build++ = '^';
  868.             *build++ = *str ^ '@';
  869.         }
  870.         else
  871.         {
  872.             *build++ = *str;
  873.         }
  874.     }
  875.     *build = '\0';
  876.     return tmpblk.c;
  877. }
  878.  
  879. /* This function displays the contents of either the map table or the
  880.  * abbreviation table.  User commands call this function as follows:
  881.  *    :map    dumpkey(WHEN_VICMD, FALSE);
  882.  *    :map!    dumpkey(WHEN_VIREP|WHEN_VIINP, FALSE);
  883.  *    :abbr    dumpkey(WHEN_VIINP|WHEN_VIREP, TRUE);
  884.  *    :abbr!    dumpkey(WHEN_EX|WHEN_VIINP|WHEN_VIREP, TRUE);
  885.  */
  886. void dumpkey(when, abbr)
  887.     int    when;    /* WHEN_XXXX of mappings to be dumped */
  888.     int    abbr;    /* boolean: dump abbreviations instead of maps? */
  889. {
  890.     MAP    *scan;
  891.     char    *str;
  892.     int    len;
  893.  
  894. #ifndef NO_ABBR
  895.     for (scan = (abbr ? abbrs : maps); scan; scan = scan->next)
  896. #else
  897.     for (scan = maps; scan; scan = scan->next)
  898. #endif
  899.     {
  900.         /* skip entries that don't match "when" */
  901.         if ((scan->flags & when) == 0)
  902.         {
  903.             continue;
  904.         }
  905.  
  906.         /* dump the key label, if any */
  907.         if (!abbr)
  908.         {
  909.             len = 8;
  910.             if (scan->label)
  911.             {
  912.                 qaddstr(scan->label);
  913.                 len -= strlen(scan->label);
  914.             }
  915.             do
  916.             {
  917.                 qaddch(' ');
  918.             } while (len-- > 0);
  919.         }
  920.  
  921.         /* dump the rawin version */
  922.         str = printable(scan->rawin);
  923.         qaddstr(str);
  924.         len = strlen(str);
  925.         do
  926.         {
  927.             qaddch(' ');
  928.         } while (len++ < 8);
  929.             
  930.         /* dump the mapped version */
  931. #ifndef NO_EXTENSIONS
  932.         if ((scan->flags & WHEN_INMV) && (when & (WHEN_VIINP|WHEN_VIREP)))
  933.         {
  934.             qaddstr("visual ");
  935.         }
  936. #endif
  937.         str = printable(scan->cooked);
  938.         qaddstr(str);
  939.         addch('\n');
  940.         exrefresh();
  941.     }
  942. }
  943.  
  944. #ifndef NO_MKEXRC
  945.  
  946. static void safequote(str)
  947.     char    *str;
  948. {
  949.     char    *build;
  950.  
  951.     build = tmpblk.c + strlen(tmpblk.c);
  952.     while (*str)
  953.     {
  954.         if (*str <= ' ' && *str >= 1 || *str == '|')
  955.         {
  956.             *build++ = ctrl('V');
  957.         }
  958.         *build++ = *str++;
  959.     }
  960.     *build = '\0';
  961. }
  962.  
  963. /* This function saves the contents of either the map table or the
  964.  * abbreviation table into a file.  Both the "bang" and "no bang" versions
  965.  * are saved.
  966.  *    :map    dumpkey(WHEN_VICMD, FALSE);
  967.  *    :map!    dumpkey(WHEN_VIREP|WHEN_VIINP, FALSE);
  968.  *    :abbr    dumpkey(WHEN_VIINP|WHEN_VIREP, TRUE);
  969.  *    :abbr!    dumpkey(WHEN_EX|WHEN_VIINP|WHEN_VIREP, TRUE);
  970.  */
  971. void
  972. savemaps(fd, abbr)
  973.     int    fd;    /* file descriptor of an open file to write to */
  974.     int    abbr;    /* boolean: do abbr table? (else do map table) */
  975. {
  976.     MAP    *scan;
  977.     int    bang;
  978.     int    when;
  979.  
  980. # ifndef NO_ABBR
  981.     for (scan = (abbr ? abbrs : maps); scan; scan = scan->next)
  982. # else
  983.     for (scan = maps; scan; scan = scan->next)
  984. # endif
  985.     {
  986.         /* skip maps that have labels, except for function keys */
  987.         if (scan->label && *scan->label != '#')
  988.         {
  989.             continue;
  990.         }
  991.  
  992.         for (bang = 0; bang < 2; bang++)
  993.         {
  994.             /* decide which "when" flags we want */
  995. # ifndef NO_ABBR
  996.             if (abbr)
  997.                 when = (bang ? WHEN_EX|WHEN_VIINP|WHEN_VIREP : WHEN_VIINP|WHEN_VIREP);
  998.             else
  999. # endif
  1000.                 when = (bang ? WHEN_VIREP|WHEN_VIINP : WHEN_VICMD);
  1001.  
  1002.             /* skip entries that don't match "when" */
  1003.             if ((scan->flags & when) == 0)
  1004.             {
  1005.                 continue;
  1006.             }
  1007.  
  1008.             /* write a "map" or "abbr" command name */
  1009. # ifndef NO_ABBR
  1010.             if (abbr)
  1011.                 strcpy(tmpblk.c, "abbr");
  1012.             else
  1013. # endif
  1014.                 strcpy(tmpblk.c, "map");
  1015.  
  1016.             /* maybe write a bang.  Definitely write a space */
  1017.             if (bang)
  1018.                 strcat(tmpblk.c, "! ");
  1019.             else
  1020.                 strcat(tmpblk.c, " ");
  1021.  
  1022.             /* write the rawin version */
  1023. # ifndef NO_FKEY
  1024.             if (scan->label)
  1025.                 strcat(tmpblk.c, scan->label);
  1026.             else
  1027. # endif
  1028.                 safequote(scan->rawin);
  1029.             strcat(tmpblk.c, " ");
  1030.                 
  1031.             /* dump the mapped version */
  1032. # ifndef NO_EXTENSIONS
  1033.             if ((scan->flags & WHEN_INMV) && (when & (WHEN_VIINP|WHEN_VIREP)))
  1034.             {
  1035.                 strcat(tmpblk.c, "visual ");
  1036.             }
  1037. # endif
  1038.             safequote(scan->cooked);
  1039.             strcat(tmpblk.c, "\n");
  1040.             twrite(fd, tmpblk.c, strlen(tmpblk.c));
  1041.         }
  1042.     }
  1043. }
  1044. #endif
  1045.