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