home *** CD-ROM | disk | FTP | other *** search
/ Fresh Fish 5 / FreshFish_July-August1994.bin / bbs / util / vim-2.0.lha / Vim-2.0 / src / buffers.c < prev    next >
Encoding:
C/C++ Source or Header  |  1993-12-15  |  28.9 KB  |  1,336 lines

  1. /* vi:ts=4:sw=4
  2.  *
  3.  * VIM - Vi IMproved
  4.  *
  5.  * Code Contributions By:    Bram Moolenaar            mool@oce.nl
  6.  *                            Tim Thompson            twitch!tjt
  7.  *                            Tony Andrews            onecom!wldrdg!tony 
  8.  *                            G. R. (Fred) Walter        watmath!watcgl!grwalter 
  9.  */
  10.  
  11. /*
  12.  * buffers.c
  13.  *
  14.  * manipulations with redo buffer and stuff buffer
  15.  */
  16.  
  17. #include "vim.h"
  18. #include "globals.h"
  19. #include "proto.h"
  20. #include "param.h"
  21.  
  22. /*
  23.  * structure used to store one block of the stuff/redo/macro buffers
  24.  */
  25. struct bufblock
  26. {
  27.         struct bufblock *b_next;        /* pointer to next bufblock */
  28.         u_char            b_str[1];        /* contents (actually longer) */
  29. };
  30.  
  31. #define MINIMAL_SIZE 20                 /* minimal size for b_str */
  32.  
  33. /*
  34.  * header used for the stuff buffer and the redo buffer
  35.  */
  36. struct buffheader
  37. {
  38.         struct bufblock bh_first;        /* first (dummy) block of list */
  39.         struct bufblock *bh_curr;        /* bufblock for appending */
  40.         int             bh_index;        /* index for reading */
  41.         int             bh_space;        /* space in bh_curr for appending */
  42. };
  43.  
  44. static struct buffheader stuffbuff = {{NULL, {NUL}}, NULL, 0, 0};
  45. static struct buffheader redobuff = {{NULL, {NUL}}, NULL, 0, 0};
  46. static struct buffheader recordbuff = {{NULL, {NUL}}, NULL, 0, 0};
  47.  
  48.     /*
  49.      * when block_redo is TRUE redo buffer will not be changed
  50.      * used by edit() to repeat insertions and 'V' command for redoing
  51.      */
  52. static int        block_redo = FALSE;
  53.  
  54. /*
  55.  * structure used for mapping
  56.  */
  57. struct mapblock
  58. {
  59.     struct mapblock *m_next;        /* next mapblock */
  60.     char            *m_keys;        /* mapped from */
  61.     int                 m_keylen;        /* strlen(m_keys) */
  62.     char            *m_str;         /* mapped to */
  63.     int              m_mode;        /* valid mode */
  64.     int                 m_noremap;        /* if non-zero no re-mapping for m_str */
  65. };
  66.  
  67. static struct mapblock maplist = {NULL, NULL, 0, NULL, 0, 0};
  68.                                     /* first dummy entry in maplist */
  69.  
  70. /*
  71.  * variables used by vgetorpeek() and flush_buffers()
  72.  *
  73.  * typestr contains all characters that are not consumed yet.
  74.  * The part in front may contain the result of mappings and @a commands.
  75.  * The lenght of this part is typemaplen.
  76.  * After it are characters that come from the terminal.
  77.  * Some parts of typestr may not be mapped. These parts are remembered in
  78.  * the noremaplist. 
  79.  */
  80. #define MAXMAPLEN 50                /* maximum length of key sequence to be mapped */
  81.                                     /* must be able to hold an Amiga resize report */
  82. static char        *typestr = NULL;    /* NUL-terminated buffer for typeahead characters */
  83. static char        typebuf[MAXMAPLEN + 3]; /* initial typestr */
  84.  
  85. static int        typemaplen = 0;        /* number of mapped characters in typestr */
  86.  
  87. /* 
  88.  * parts int typestr that should not be mapped are remembered with a list
  89.  * of noremap structs. Noremaplist is the first.
  90.  */
  91. struct noremap
  92. {
  93.     int                nr_off;            /* offset to not remappable chars */
  94.     int                nr_len;            /* number of not remappable chars */
  95.     struct noremap    *nr_next;        /* next entry in the list */
  96. };
  97.  
  98. static struct noremap noremaplist = {0, 0, NULL};
  99.  
  100. static void        free_buff __ARGS((struct buffheader *));
  101. static u_char    *get_bufcont __ARGS((struct buffheader *, int));
  102. static void        add_buff __ARGS((struct buffheader *, char *));
  103. static void        add_num_buff __ARGS((struct buffheader *, long));
  104. static void        add_char_buff __ARGS((struct buffheader *, int));
  105. static u_char    read_stuff __ARGS((int));
  106. static int        start_stuff __ARGS((void));
  107. static int        read_redo __ARGS((int));
  108. static void        gotchars __ARGS((char *, int));
  109. static void        init_typestr __ARGS((void));
  110. static u_char    vgetorpeek __ARGS((int));
  111. static void        showmap __ARGS((struct mapblock *));
  112.  
  113. /*
  114.  * free and clear a buffer
  115.  */
  116.     static void
  117. free_buff(buf)
  118.     struct buffheader *buf;
  119. {
  120.         register struct bufblock *p, *np;
  121.  
  122.         for (p = buf->bh_first.b_next; p != NULL; p = np)
  123.         {
  124.                 np = p->b_next;
  125.                 free((char *)p);
  126.         }
  127.         buf->bh_first.b_next = NULL;
  128. }
  129.  
  130. /*
  131.  * return the contents of a buffer as a single string
  132.  */
  133.     static u_char *
  134. get_bufcont(buffer, dozero)
  135.     struct buffheader    *buffer;
  136.     int                    dozero;        /* count == zero is not an error */
  137. {
  138.         u_long            count = 0;
  139.         u_char            *p = NULL;
  140.         struct bufblock    *bp;
  141.  
  142. /* compute the total length of the string */
  143.         for (bp = buffer->bh_first.b_next; bp != NULL; bp = bp->b_next)
  144.                 count += strlen((char *)bp->b_str);
  145.  
  146.         if ((count || dozero) && (p = (u_char *)lalloc(count + 1, TRUE)) != NULL)
  147.         {
  148.                 *p = NUL;
  149.                 for (bp = buffer->bh_first.b_next; bp != NULL; bp = bp->b_next)
  150.                         strcat((char *)p, (char *)bp->b_str);
  151.         }
  152.         return (p);
  153. }
  154.  
  155. /*
  156.  * return the contents of the record buffer as a single string
  157.  *    and clear the record buffer
  158.  */
  159.     u_char *
  160. get_recorded()
  161. {
  162.     u_char *p;
  163.  
  164.     p = get_bufcont(&recordbuff, TRUE);
  165.     free_buff(&recordbuff);
  166.     return (p);
  167. }
  168.  
  169. /*
  170.  * return the contents of the redo buffer as a single string
  171.  */
  172.     u_char *
  173. get_inserted()
  174. {
  175.         return(get_bufcont(&redobuff, FALSE));
  176. }
  177.  
  178. /*
  179.  * add string "s" after the current block of buffer "buf"
  180.  */
  181.     static void
  182. add_buff(buf, s)
  183.     register struct buffheader    *buf;
  184.     char                        *s;
  185. {
  186.     struct bufblock *p;
  187.     u_long             n;
  188.     u_long             len;
  189.  
  190.     if ((n = strlen(s)) == 0)                /* don't add empty strings */
  191.         return;
  192.  
  193.     if (buf->bh_first.b_next == NULL)        /* first add to list */
  194.     {
  195.         buf->bh_space = 0;
  196.         buf->bh_curr = &(buf->bh_first);
  197.     }
  198.     else if (buf->bh_curr == NULL)            /* buffer has already been read */
  199.     {
  200.         emsg("Add to read buffer");
  201.         return;
  202.     }
  203.     else if (buf->bh_index != 0)
  204.         strcpy((char *)buf->bh_first.b_next->b_str, (char *)buf->bh_first.b_next->b_str + buf->bh_index);
  205.     buf->bh_index = 0;
  206.  
  207.     if (buf->bh_space >= n)
  208.     {
  209.         strcat((char *)buf->bh_curr->b_str, s);
  210.         buf->bh_space -= n;
  211.     }
  212.     else
  213.     {
  214.         if (n < MINIMAL_SIZE)
  215.             len = MINIMAL_SIZE;
  216.         else
  217.             len = n;
  218.         p = (struct bufblock *)lalloc((u_long)(sizeof(struct bufblock) + len), TRUE);
  219.         if (p == NULL)
  220.             return; /* no space, just forget it */
  221.         buf->bh_space = len - n;
  222.         strcpy((char *)p->b_str, s);
  223.  
  224.         p->b_next = buf->bh_curr->b_next;
  225.         buf->bh_curr->b_next = p;
  226.         buf->bh_curr = p;
  227.     }
  228.     return;
  229. }
  230.  
  231.     static void
  232. add_num_buff(buf, n)
  233.     struct buffheader *buf;
  234.     long               n;
  235. {
  236.         char    number[32];
  237.  
  238.         sprintf(number, "%ld", n);
  239.         add_buff(buf, number);
  240. }
  241.  
  242.     static void
  243. add_char_buff(buf, c)
  244.     struct buffheader *buf;
  245.     int               c;
  246. {
  247.         char    temp[2];
  248.  
  249.         temp[0] = c;
  250.         temp[1] = NUL;
  251.         add_buff(buf, temp);
  252. }
  253.  
  254. /*
  255.  * get one character from the stuff buffer
  256.  * If advance == TRUE go to the next char.
  257.  */
  258.     static u_char
  259. read_stuff(advance)
  260.     int            advance;
  261. {
  262.         register u_char c;
  263.         register struct bufblock *curr;
  264.  
  265.  
  266.         if (stuffbuff.bh_first.b_next == NULL)    /* buffer is empty */
  267.             return NUL;
  268.  
  269.         curr = stuffbuff.bh_first.b_next;
  270.         c = curr->b_str[stuffbuff.bh_index];
  271.  
  272.         if (advance)
  273.         {
  274.             if (curr->b_str[++stuffbuff.bh_index] == NUL)
  275.             {
  276.                 stuffbuff.bh_first.b_next = curr->b_next;
  277.                 free((char *)curr);
  278.                 stuffbuff.bh_index = 0;
  279.             }
  280.         }
  281.         return c;
  282. }
  283.  
  284. /*
  285.  * prepare stuff buffer for reading (if it contains something)
  286.  */
  287.     static int
  288. start_stuff()
  289. {
  290.     if (stuffbuff.bh_first.b_next == NULL)
  291.         return FALSE;
  292.     stuffbuff.bh_curr = &(stuffbuff.bh_first);
  293.     stuffbuff.bh_space = 0;
  294.     return TRUE;
  295. }
  296.  
  297. /*
  298.  * check if the stuff buffer is empty
  299.  */
  300.     int
  301. stuff_empty()
  302. {
  303.     return (stuffbuff.bh_first.b_next == NULL);
  304. }
  305.  
  306. /*
  307.  * Remove the contents of the stuff buffer and the mapped characters in the
  308.  * typeahead buffer (used in case of an error). If 'typeahead' is true,
  309.  * flush all typeahead characters (used when interrupted by a CTRL-C).
  310.  */
  311.     void
  312. flush_buffers(typeahead)
  313.     int typeahead;
  314. {
  315.     struct noremap *p;
  316.  
  317.     init_typestr();
  318.  
  319.     start_stuff();
  320.     while (read_stuff(TRUE) != NUL)
  321.         ;
  322.  
  323.     if (typeahead)            /* remove all typeahead */
  324.     {
  325.             /*
  326.              * We have to get all characters, because we may delete the first
  327.              * part of an escape sequence.
  328.              * In an xterm we get one char at a time and we have to get them all.
  329.              */
  330.         while (inchar(typestr, MAXMAPLEN, 10))    
  331.             ;
  332.         *typestr = NUL;
  333.     }
  334.     else                    /* remove mapped characters only */
  335.         strcpy(typestr, typestr + typemaplen);
  336.     typemaplen = 0;
  337.     noremaplist.nr_len = 0;
  338.     noremaplist.nr_off = 0;
  339.     while (noremaplist.nr_next)
  340.     {
  341.         p = noremaplist.nr_next->nr_next;
  342.         free(noremaplist.nr_next);
  343.         noremaplist.nr_next = p;
  344.     }
  345. }
  346.  
  347.     void
  348. ResetRedobuff()
  349. {
  350.     if (!block_redo)
  351.         free_buff(&redobuff);
  352. }
  353.  
  354.     void
  355. AppendToRedobuff(s)
  356.     char           *s;
  357. {
  358.     if (!block_redo)
  359.         add_buff(&redobuff, s);
  360. }
  361.  
  362.     void
  363. AppendCharToRedobuff(c)
  364.     int               c;
  365. {
  366.     if (!block_redo)
  367.         add_char_buff(&redobuff, c);
  368. }
  369.  
  370.     void
  371. AppendNumberToRedobuff(n)
  372.     long             n;
  373. {
  374.     if (!block_redo)
  375.         add_num_buff(&redobuff, n);
  376. }
  377.  
  378.     void
  379. stuffReadbuff(s)
  380.     char           *s;
  381. {
  382.     add_buff(&stuffbuff, s);
  383. }
  384.  
  385.     void
  386. stuffcharReadbuff(c)
  387.     int               c;
  388. {
  389.     add_char_buff(&stuffbuff, c);
  390. }
  391.  
  392.     void
  393. stuffnumReadbuff(n)
  394.     long    n;
  395. {
  396.     add_num_buff(&stuffbuff, n);
  397. }
  398.  
  399. /*
  400.  * Read a character from the redo buffer.
  401.  * The redo buffer is left as it is.
  402.  */
  403.     static int
  404. read_redo(init)
  405.     int            init;
  406. {
  407.     static struct bufblock    *bp;
  408.     static u_char            *p;
  409.     int                        c;
  410.  
  411.     if (init)
  412.     {
  413.         if ((bp = redobuff.bh_first.b_next) == NULL)
  414.             return TRUE;
  415.         p = bp->b_str;
  416.         return FALSE;
  417.     }
  418.     if ((c = *p) != NUL)
  419.     {
  420.         if (*++p == NUL && bp->b_next != NULL)
  421.         {
  422.             bp = bp->b_next;
  423.             p = bp->b_str;
  424.         }
  425.     }
  426.     return c;
  427. }
  428.  
  429. /*
  430.  * copy the rest of the redo buffer into the stuff buffer (could be done faster)
  431.  */
  432.     void
  433. copy_redo()
  434. {
  435.     register int c;
  436.  
  437.     while ((c = read_redo(FALSE)) != NUL)
  438.         stuffcharReadbuff(c);
  439. }
  440.  
  441. extern int redo_Visual_busy;        /* this is in normal.c */
  442.  
  443. /*
  444.  * Stuff the redo buffer into the stuffbuff.
  445.  * Insert the redo count into the command.
  446.  */
  447.     int
  448. start_redo(count)
  449.     long count;
  450. {
  451.     register int c;
  452.  
  453.     if (read_redo(TRUE))    /* init the pointers; return if nothing to redo */
  454.         return FALSE;
  455.  
  456.     c = read_redo(FALSE);
  457.  
  458. /* copy the buffer name, if present */
  459.     if (c == '"')
  460.     {
  461.         add_buff(&stuffbuff, "\"");
  462.         c = read_redo(FALSE);
  463.  
  464. /* if a numbered buffer is used, increment the number */
  465.         if (c >= '1' && c < '9')
  466.             ++c;
  467.         add_char_buff(&stuffbuff, c);
  468.         c = read_redo(FALSE);
  469.     }
  470.  
  471.     if (c == 'v')    /* redo Visual */
  472.     {
  473.         Visual = Curpos;
  474.         redo_Visual_busy = TRUE;
  475.         c = read_redo(FALSE);
  476.     }
  477.  
  478. /* try to enter the count (in place of a previous count) */
  479.     if (count)
  480.     {
  481.         while (isdigit(c))        /* skip "old" count */
  482.             c = read_redo(FALSE);
  483.         add_num_buff(&stuffbuff, count);
  484.     }
  485.  
  486. /* copy from the redo buffer into the stuff buffer */
  487.     add_char_buff(&stuffbuff, c);
  488.     copy_redo();
  489.     return TRUE;
  490. }
  491.  
  492. /*
  493.  * Repeat the last insert (R, o, O, a, A, i or I command) by stuffing
  494.  * the redo buffer into the stuffbuff.
  495.  */
  496.     int
  497. start_redo_ins()
  498. {
  499.     register u_char c;
  500.  
  501.     if (read_redo(TRUE))
  502.         return FALSE;
  503.     start_stuff();
  504.  
  505. /* skip the count and the command character */
  506.     while ((c = read_redo(FALSE)) != NUL)
  507.     {
  508.         c = TO_UPPER(c);
  509.         if (strchr("AIRO", c) != NULL)
  510.         {
  511.             if (c == 'O')
  512.                 stuffReadbuff(NL_STR);
  513.             break;
  514.         }
  515.     }
  516.  
  517. /* copy the typed text from the redo buffer into the stuff buffer */
  518.     copy_redo();
  519.     block_redo = TRUE;
  520.     return TRUE;
  521. }
  522.  
  523.     void
  524. set_redo_ins()
  525. {
  526.     block_redo = TRUE;
  527. }
  528.  
  529.     void
  530. stop_redo_ins()
  531. {
  532.     block_redo = FALSE;
  533. }
  534.  
  535. /*
  536.  * insert a string in front of the typeahead buffer (for '@' command and vgetorpeek)
  537.  */
  538.     int
  539. ins_typestr(str, noremap)
  540.     char    *str;
  541.     int        noremap;
  542. {
  543.     register char    *s;
  544.     register int    newlen;
  545.     register int    addlen;
  546.  
  547.     init_typestr();
  548.  
  549.     /*
  550.      * In typestr there must always be room for MAXMAPLEN + 3 characters
  551.      */
  552.     addlen = strlen(str);
  553.     newlen = strlen(typestr) + addlen + MAXMAPLEN + 3;
  554.     if (newlen < 0)                /* string is getting too long */
  555.     {
  556.         emsg(e_toocompl);        /* also calls flush_buffers */
  557.         setcursor();
  558.         return -1;
  559.     }
  560.     s = alloc(newlen);
  561.     if (s == NULL)                /* out of memory */
  562.         return -1;
  563.  
  564.     strcpy(s, str);
  565.     strcat(s, typestr);
  566.     if (typestr != typebuf)
  567.         free(typestr);
  568.     typestr = s;
  569.     typemaplen += addlen;        /* the inserted string is not typed */
  570.     if (noremap)
  571.     {
  572.         if (noremaplist.nr_off == 0)
  573.             noremaplist.nr_len += addlen;
  574.         else
  575.         {
  576.             struct noremap *p;
  577.  
  578.             p = (struct noremap *)alloc((int)sizeof(struct noremap));
  579.             if (p != NULL)
  580.             {
  581.                 p->nr_next = noremaplist.nr_next;
  582.                 p->nr_off = noremaplist.nr_off;
  583.                 p->nr_len = noremaplist.nr_len;
  584.                 noremaplist.nr_next = p;
  585.                 noremaplist.nr_len = addlen;
  586.                 noremaplist.nr_off = 0;
  587.             }
  588.         }
  589.     }
  590.     else if (noremaplist.nr_len)
  591.         noremaplist.nr_off += addlen;
  592.     return 0;
  593. }
  594.  
  595. /*
  596.  * remove "len" characters from the front of typestr
  597.  */
  598.     void
  599. del_typestr(len)
  600.     int    len;
  601. {
  602.     struct noremap *p;
  603.  
  604.     strcpy(typestr, typestr + len);        /* remove chars from the buffer */
  605.     if ((typemaplen -= len) <= 0)        /* adjust typemaplen */
  606.         typemaplen = 0;
  607.     while (len)                            /* adjust noremaplist */
  608.     {
  609.         if (noremaplist.nr_off >= len)
  610.         {
  611.             noremaplist.nr_off -= len;
  612.             break;
  613.         }
  614.         len -= noremaplist.nr_off;
  615.         noremaplist.nr_off = 0;
  616.         if (noremaplist.nr_len > len)
  617.         {
  618.             noremaplist.nr_len -= len;
  619.             break;
  620.         }
  621.         len -= noremaplist.nr_len;
  622.         p = noremaplist.nr_next;
  623.         if (p == NULL)
  624.         {
  625.             noremaplist.nr_len = 0;
  626.             break;
  627.         }
  628.         noremaplist.nr_next = p->nr_next;
  629.         noremaplist.nr_len = p->nr_len;
  630.         noremaplist.nr_off = p->nr_off;
  631.         free(p);
  632.     }
  633. }
  634.  
  635. extern int arrow_used;            /* this is in edit.c */
  636.  
  637. /*
  638.  * Write typed characters to script file.
  639.  * If recording is on put the character in the recordbuffer.
  640.  */
  641.     static void
  642. gotchars(s, len)
  643.     char    *s;
  644.     int        len;
  645. {
  646.     while (len--)
  647.     {
  648.         updatescript(*s & 255);
  649.  
  650.         if (Recording)
  651.             add_char_buff(&recordbuff, (*s & 255));
  652.         ++s;
  653.     }
  654.  
  655.             /* do not sync in insert mode, unless cursor key has been used */
  656.     if (!(State & (INSERT + CMDLINE)) || arrow_used)        
  657.         u_sync();
  658. }
  659.  
  660. /*
  661.  * Initialize typestr to point to typebuf.
  662.  * Alloc() cannot be used here: In out-of-memory situations it would
  663.  * be impossible to type anything.
  664.  */
  665.     static void
  666. init_typestr()
  667. {
  668.     if (typestr == NULL)
  669.     {
  670.         typestr = typebuf;
  671.         typebuf[0] = NUL;
  672.     }
  673. }
  674.  
  675. #define NEEDMORET 9999        /* value for incomplete mapping or key-code */
  676.  
  677. /*
  678.  * get a character: 1. from the stuffbuffer
  679.  *                    2. from the typeahead buffer
  680.  *                    3. from the user
  681.  *
  682.  * KeyTyped is set to TRUE in the case the user typed the key.
  683.  * If advance is TRUE, we really get the character. Otherwise we just look
  684.  * whether there is a character available.
  685.  */
  686.     static u_char
  687. vgetorpeek(advance)
  688.     int        advance;
  689. {
  690.     register int    c;
  691.     int                n = 0;        /* init for GCC */
  692.     int                len;
  693. #ifdef AMIGA
  694.     char            *s;
  695. #endif
  696.     register struct mapblock *mp;
  697.     int                timedout = FALSE;    /* waited for more than 1 second
  698.                                                 for mapping to complete */
  699.     int                mapdepth = 0;        /* check for recursive mapping */
  700.     int                mode_deleted = FALSE;    /* set when mode has been deleted */
  701.  
  702.     init_typestr();
  703.     start_stuff();
  704.     if (typemaplen == 0)
  705.         Exec_reg = FALSE;
  706.     do
  707.     {
  708.         c = read_stuff(advance);
  709.         if (c != NUL && !got_int)
  710.             KeyTyped = FALSE;
  711.         else
  712.         {
  713.             /*
  714.              * Loop until we either find a matching mapped key, or we
  715.              * are sure that it is not a mapped key.
  716.              * If a mapped key sequence is found we go back to the start to
  717.              * try re-mapping.
  718.              */
  719.  
  720.             for (;;)
  721.             {
  722.                 len = strlen(typestr);
  723.                 breakcheck();                /* check for CTRL-C */
  724.                 if (got_int)
  725.                 {
  726.                     c = inchar(typestr, MAXMAPLEN, 0);    /* flush all input */
  727.                     /*
  728.                      * If inchar returns TRUE (script file was active) or we are
  729.                      * inside a mapping, get out of insert mode.
  730.                      * Otherwise we behave like having gotten a CTRL-C.
  731.                      * As a result typing CTRL-C in insert mode will
  732.                      * really insert a CTRL-C.
  733.                      */
  734.                     if ((c || typemaplen) && (State & (INSERT + CMDLINE)))
  735.                         c = ESC;
  736.                     else
  737.                         c = Ctrl('C');
  738.                     flush_buffers(TRUE);        /* flush all typeahead */
  739.                     break;
  740.                 }
  741.                 else if (len > 0)    /* see if we have a mapped key sequence */
  742.                 {
  743.                     /*
  744.                      * walk through the maplist until we find an
  745.                      * entry that matches (if not timed out).
  746.                      */
  747.                     mp = NULL;
  748.                     if (!timedout && (typemaplen == 0 || (p_remap &&
  749.                             (noremaplist.nr_len == 0 || noremaplist.nr_off != 0)))
  750.                             && !((State & (INSERT + CMDLINE)) && p_paste))
  751.                     {
  752.                         for (mp = maplist.m_next; mp; mp = mp->m_next)
  753.                         {
  754.                             if ((mp->m_mode & ABBREV) || !(mp->m_mode & State))
  755.                                 continue;
  756.                             n = mp->m_keylen;
  757.                             if (noremaplist.nr_off != 0 && n > noremaplist.nr_off)
  758.                                 continue;
  759.                             if (!strncmp(mp->m_keys, typestr, (size_t)(n > len ? len : n)))
  760.                                 break;
  761.                         }
  762.                     }
  763.                     if (mp == NULL)                    /* no match found */
  764.                     {
  765.                             /*
  766.                              * check if we have a terminal code, when
  767.                              *    mapping is allowed,
  768.                              *  keys have not been mapped,
  769.                              *    and not an ESC sequence, not in insert mode or
  770.                              *        p_ek is on,
  771.                              *    and when not timed out,
  772.                              */
  773.                         if (State != NOMAPPING &&
  774.                                 typemaplen == 0 &&
  775.                                 (typestr[0] != ESC || p_ek || !(State & INSERT)) &&
  776.                                 !timedout)
  777.                             n = check_termcode(typestr);
  778.                         else
  779.                             n = 0;
  780.                         if (n == 0)        /* no matching terminal code */
  781.                         {
  782. #ifdef AMIGA                    /* check for window bounds report */
  783.                             if (typemaplen == 0 && (typestr[0] & 0xff) == CSI)
  784.                             {
  785.                                 for (s = typestr + 1; isdigit(*s) || *s == ';' || *s == ' '; ++s)
  786.                                     ;
  787.                                 if (*s == 'r' || *s == '|')    /* found one */
  788.                                 {
  789.                                     strcpy(typestr, s + 1);
  790.                                     set_winsize(0, 0, FALSE);        /* get size and redraw screen */
  791.                                     continue;
  792.                                 }
  793.                                 if (*s == NUL)        /* need more characters */
  794.                                     n = -1;
  795.                             }
  796.                             if (n != -1)            /* got a single character */
  797. #endif
  798.                             {
  799.                                 c = typestr[0] & 255;
  800.                                 if (typemaplen)
  801.                                     KeyTyped = FALSE;
  802.                                 else
  803.                                 {
  804.                                     KeyTyped = TRUE;
  805.                                     if (advance)    /* write char to script file(s) */
  806.                                         gotchars(typestr, 1);
  807.                                 }
  808.                                 if (advance)        /* remove chars from typestr */
  809.                                     del_typestr(1);
  810.                                 break;        /* got character, break for loop */
  811.                             }
  812.                         }
  813.                         if (n > 0)        /* full matching terminal code */
  814.                             continue;    /* try mapping again */
  815.  
  816.                         /* partial match: get some more characters */
  817.                         n = NEEDMORET;
  818.                     }
  819.                     if (n <= len)        /* complete match */
  820.                     {
  821.                         if (n > typemaplen)        /* write chars to script file(s) */
  822.                             gotchars(typestr + typemaplen, n - typemaplen);
  823.  
  824.                         del_typestr(n);    /* remove the mapped keys */
  825.  
  826.                         /*
  827.                          * Put the replacement string in front of mapstr.
  828.                          * The depth check catches ":map x y" and ":map y x".
  829.                          */
  830.                         if (++mapdepth == 1000)
  831.                         {
  832.                             emsg("recursive mapping");
  833.                             if (State == CMDLINE)
  834.                                 redrawcmdline();
  835.                             else
  836.                                 setcursor();
  837.                             flush_buffers(FALSE);
  838.                             mapdepth = 0;        /* for next one */
  839.                             c = -1;
  840.                             break;
  841.                         }
  842.                         if (ins_typestr(mp->m_str, mp->m_noremap) < 0)
  843.                         {
  844.                             c = -1;
  845.                             break;
  846.                         }
  847.                         continue;
  848.                     }
  849.                 }
  850.                 /*
  851.                  * special case: if we get an <ESC> in insert mode and there are
  852.                  * no more characters at once, we pretend to go out of insert mode.
  853.                  * This prevents the one second delay after typing an <ESC>.
  854.                  * If we get something after all, we may have to redisplay the
  855.                  * mode. That the cursor is in the wrong place does not matter.
  856.                  */
  857.                 c = 0;
  858.                 if (advance && len == 1 && typestr[0] == ESC && typemaplen == 0 && (State & INSERT) && (p_timeout || (n == NEEDMORET && p_ttimeout)) && (c = inchar(typestr + len, 2, 0)) == 0)
  859.                 {
  860.                     if (p_smd)
  861.                     {
  862.                         delmode();
  863.                         mode_deleted = TRUE;
  864.                     }
  865.                     if (Curscol)        /* move cursor one left if possible */
  866.                         --Curscol;
  867.                     else if (p_wrap && Curpos.col != 0 && Cursrow)
  868.                     {
  869.                             --Cursrow;
  870.                             Curscol = Columns - 1;
  871.                     }
  872.                     setcursor();
  873.                     flushbuf();
  874.                 }
  875.                 len += c;
  876.  
  877.                 if (len >= typemaplen + MAXMAPLEN)    /* buffer full, don't map */
  878.                 {
  879.                     timedout = TRUE;
  880.                     continue;
  881.                 }
  882.                 c = inchar(typestr + len, typemaplen + MAXMAPLEN - len, !advance ? 0 : ((len == 0 || !(p_timeout || (p_ttimeout && n == NEEDMORET))) ? -1 : (int)p_tm));
  883.                 if (c <= NUL)        /* no character available */
  884.                 {
  885.                     if (!advance)
  886.                         break;
  887.                     if (len)                /* timed out */
  888.                     {
  889.                         timedout = TRUE;
  890.                         continue;
  891.                     }
  892.                 }
  893.                 else
  894.                 {
  895.                     if (mode_deleted)        /* character entered after ESC */
  896.                     {
  897.                         showmode();
  898.                         mode_deleted = FALSE;
  899.                     }
  900.                 }
  901.             }        /* for (;;) */
  902.         }        /* if (!character from stuffbuf) */
  903.  
  904.                         /* if advance is FALSE don't loop on NULs */
  905.     } while (c < 0 || (advance && c == NUL));
  906.  
  907.         /* delete "INSERT" message if we return an ESC */
  908.     if (c == ESC && p_smd && !mode_deleted && (State & INSERT))
  909.         delmode();
  910.  
  911.     return (u_char) c;
  912. }
  913.  
  914.     u_char
  915. vgetc()
  916. {
  917.     return (vgetorpeek(TRUE));
  918. }
  919.  
  920.     u_char
  921. vpeekc()
  922. {
  923.     return (vgetorpeek(FALSE));
  924. }
  925.  
  926. /*
  927.  * map[!]                    : show all key mappings
  928.  * map[!] {lhs}                : show key mapping for {lhs}
  929.  * map[!] {lhs} {rhs}        : set key mapping for {lhs} to {rhs}
  930.  * noremap[!] {lhs} {rhs}    : same, but no remapping for {rhs}
  931.  * unmap[!] {lhs}            : remove key mapping for {lhs}
  932.  * abbr                        : show all abbreviations
  933.  * abbr {lhs}                : show abbreviations for {lhs}
  934.  * abbr {lhs} {rhs}            : set abbreviation for {lhs} to {rhs}
  935.  * noreabbr {lhs} {rhs}        : same, but no remapping for {rhs}
  936.  * unabbr {lhs}                : remove abbreviation for {lhs}
  937.  *
  938.  * maptype == 1 for unmap command, 2 for noremap command.
  939.  *
  940.  * keys is pointer to any arguments.
  941.  *
  942.  * for :map      mode is NORMAL 
  943.  * for :map!  mode is INSERT + CMDLINE
  944.  * for :cmap  mode is CMDLINE
  945.  * for :imap  mode is INSERT 
  946.  * for :abbr  mode is INSERT + CMDLINE + ABBREV
  947.  * for :iabbr mode is INSERT + ABBREV
  948.  * for :cabbr mode is CMDLINE + ABBREV
  949.  * 
  950.  * Return 0 for success
  951.  *          1 for invalid arguments
  952.  *          2 for no match
  953.  *          3 for ambiguety
  954.  *          4 for out of mem
  955.  */
  956.     int
  957. domap(maptype, keys, mode)
  958.     int        maptype;
  959.     char    *keys;
  960.     int        mode;
  961. {
  962.     struct mapblock        *mp, *mprev;
  963.     char                *arg;
  964.     char                *p;
  965.     int                    n = 0;            /* init for GCC */
  966.     int                    len = 0;        /* init for GCC */
  967.     char                *newstr;
  968.     int                    hasarg;
  969.     int                    haskey;
  970.     int                    did_it = FALSE;
  971.     int                    abbrev = 0;
  972.     int                    round;
  973.  
  974.     if (mode & ABBREV)        /* not a mapping but an abbreviation */
  975.     {
  976.         abbrev = ABBREV;
  977.         mode &= ~ABBREV;
  978.     }
  979. /*
  980.  * find end of keys and remove CTRL-Vs in it
  981.  */
  982.     p = keys;
  983.     while (*p && *p != ' ' && *p != '\t')
  984.     {
  985.         if (*p == Ctrl('V') && p[1] != NUL)
  986.             strcpy(p, p + 1);            /* remove CTRL-V */
  987.         ++p;
  988.     }
  989.     if (*p != NUL)
  990.         *p++ = NUL;
  991.     skipspace(&p);
  992.     arg = p;
  993.     hasarg = (*arg != NUL);
  994.     haskey = (*keys != NUL);
  995.  
  996.         /* check for :unmap with not one argument */
  997.     if (maptype == 1 && (!haskey || hasarg))    
  998.         return 1;
  999.  
  1000. /*
  1001.  * remove CTRL-Vs from argument
  1002.  */
  1003.     while (*p)
  1004.     {
  1005.         if (*p == Ctrl('V') && p[1] != NUL)
  1006.             strcpy(p, p + 1);            /* remove CTRL-V */
  1007.         ++p;
  1008.     }
  1009.  
  1010. /*
  1011.  * check arguments and translate function keys
  1012.  */
  1013.     if (haskey)
  1014.     {
  1015.         if (*keys == '#' && isdigit(*(keys + 1)))    /* function key */
  1016.         {
  1017.             if (*++keys == '0')
  1018.                 *(u_char *)keys = K_F10;
  1019.             else
  1020.                 *keys += K_F1 - '1';
  1021.         }
  1022.         len = strlen(keys);
  1023.         if (len > MAXMAPLEN)            /* maximum lenght of MAXMAPLEN chars */
  1024.             return 2;
  1025.     }
  1026.  
  1027.     if (haskey && hasarg && abbrev)        /* will add an abbreviation */
  1028.         no_abbr = FALSE;
  1029.  
  1030. #ifdef AMIGA
  1031.     if (!haskey || (maptype != 1 && !hasarg))
  1032.         settmode(0);                /* set cooked mode so output can be halted */
  1033. #endif
  1034. /*
  1035.  * Find an entry in the maplist that matches.
  1036.  * For :unmap we may loop two times: once to try to unmap an entry with a
  1037.  * matching 'from' part, a second time, if the first fails, to unmap an
  1038.  * entry with a matching 'to' part. This was done to allow ":ab foo bar" to be
  1039.  * unmapped by typing ":unab foo", where "foo" will be replaced by "bar" because
  1040.  * of the abbreviation.
  1041.  */
  1042.     for (round = 0; (round == 0 || maptype == 1) && round <= 1 && !did_it; ++round)
  1043.     {
  1044.         for (mp = maplist.m_next, mprev = &maplist; mp; mprev = mp, mp = mp->m_next)
  1045.         {
  1046.                                         /* skip entries with wrong mode */
  1047.             if (!(mp->m_mode & mode) || (mp->m_mode & ABBREV) != abbrev)
  1048.                 continue;
  1049.             if (!haskey)                        /* show all entries */
  1050.             {
  1051.                 showmap(mp);
  1052.                 did_it = TRUE;
  1053.             }
  1054.             else                                /* do we have a match? */
  1055.             {
  1056.                 if (round)        /* second round: try 'to' string for unmap */
  1057.                 {
  1058.                     n = strlen(mp->m_str);
  1059.                     p = mp->m_str;
  1060.                 }
  1061.                 else
  1062.                 {
  1063.                     n = mp->m_keylen;
  1064.                     p = mp->m_keys;
  1065.                 }
  1066.                 if (!strncmp(p, keys, (size_t)(n < len ? n : len)))
  1067.                 {
  1068.                     if (maptype == 1)            /* delete entry */
  1069.                     {
  1070.                         if (n != len)            /* not a full match */
  1071.                             continue;
  1072.                         /*
  1073.                          * We reset the indicated mode bits. If nothing is left the
  1074.                          * entry is deleted below.
  1075.                          */
  1076.                         mp->m_mode &= (~mode | ABBREV);
  1077.                         did_it = TRUE;            /* remember that we did something */
  1078.                     }
  1079.                     else if (!hasarg)            /* show matching entry */
  1080.                     {
  1081.                         showmap(mp);
  1082.                         did_it = TRUE;
  1083.                     }
  1084.                     else if (n != len)            /* new entry is ambigious */
  1085.                     {
  1086.                         return 3;
  1087.                     }
  1088.                     else
  1089.                     {
  1090.                         mp->m_mode &= (~mode | ABBREV);        /* remove mode bits */
  1091.                         if (!(mp->m_mode & ~ABBREV) && !did_it)    /* reuse existing entry */
  1092.                         {
  1093.                             newstr = strsave(arg);
  1094.                             if (newstr == NULL)
  1095.                                 return 4;            /* no mem */
  1096.                             free(mp->m_str);
  1097.                             mp->m_str = newstr;
  1098.                             mp->m_noremap = maptype;
  1099.                             mp->m_mode = mode + abbrev;
  1100.                             did_it = TRUE;
  1101.                         }
  1102.                     }
  1103.                     if (!(mp->m_mode & ~ABBREV))        /* entry can be deleted */
  1104.                     {
  1105.                         free(mp->m_keys);
  1106.                         free(mp->m_str);
  1107.                         mprev->m_next = mp->m_next;
  1108.                         free(mp);
  1109.                         mp = mprev;                    /* continue with next entry */
  1110.                     }
  1111.                 }
  1112.             }
  1113.         }
  1114.     }
  1115.  
  1116.     if (maptype == 1)                        /* delete entry */
  1117.     {
  1118.         if (did_it)
  1119.             return 0;                        /* removed OK */
  1120.         else
  1121.             return 2;                        /* no match */
  1122.     }
  1123.  
  1124.     if (!haskey || !hasarg)                    /* print entries */
  1125.     {
  1126. #ifdef AMIGA
  1127.         settmode(1);
  1128. #endif
  1129.         if (did_it)
  1130.             wait_return(TRUE);
  1131.         else if (abbrev)
  1132.             msg("No abbreviation found");
  1133.         else
  1134.             msg("No mapping found");
  1135.         return 0;                            /* listing finished */
  1136.     }
  1137.  
  1138.     if (did_it)                    /* have added the new entry already */
  1139.         return 0;
  1140. /*
  1141.  * get here when we have to add a new entry
  1142.  */
  1143.         /* allocate a new entry for the maplist */
  1144.     mp = (struct mapblock *)alloc((unsigned)sizeof(struct mapblock));
  1145.     if (mp == NULL)
  1146.         return 4;            /* no mem */
  1147.     mp->m_keys = strsave(keys);
  1148.     mp->m_str = strsave(arg);
  1149.     if (mp->m_keys == NULL || mp->m_str == NULL)
  1150.     {
  1151.             free(mp->m_keys);
  1152.             free(mp->m_str);
  1153.             free(mp);
  1154.             return 4;        /* no mem */
  1155.     }
  1156.     mp->m_keylen = strlen(mp->m_keys);
  1157.     mp->m_noremap = maptype;
  1158.     mp->m_mode = mode + abbrev;
  1159.  
  1160.     /* add the new entry in front of the maplist */
  1161.     mp->m_next = maplist.m_next;
  1162.     maplist.m_next = mp;
  1163.  
  1164.     return 0;                /* added OK */
  1165. }
  1166.  
  1167.     static void
  1168. showmap(mp)
  1169.     struct mapblock *mp;
  1170. {
  1171.     int len;
  1172.  
  1173.     if ((mp->m_mode & (INSERT + CMDLINE)) == INSERT + CMDLINE)
  1174.         outstr("! ");
  1175.     else if (mp->m_mode & INSERT)
  1176.         outstr("i ");
  1177.     else if (mp->m_mode & CMDLINE)
  1178.         outstr("c ");
  1179.     len = outtrans(mp->m_keys, -1);    /* get length of what we have written */
  1180.     do
  1181.     {
  1182.         outchar(' ');                /* padd with blanks */
  1183.         ++len;
  1184.     } while (len < 12);
  1185.     if (mp->m_noremap)
  1186.         outchar('*');
  1187.     else
  1188.         outchar(' ');
  1189.     outtrans(mp->m_str, -1);
  1190.     outchar('\n');
  1191.     flushbuf();
  1192. }
  1193.  
  1194. /*
  1195.  * Check for an abbreviation.
  1196.  * Cursor is at ptr[col]. When inserting, mincol is where insert started.
  1197.  * "c" is the character typed before check_abbr was called.
  1198.  */
  1199.     int
  1200. check_abbr(c, ptr, col, mincol)
  1201.     int        c;
  1202.     char    *ptr;
  1203.     int        col;
  1204.     int        mincol;
  1205. {
  1206.     int                len;
  1207.     int                j;
  1208.     char            tb[3];
  1209.     struct mapblock *mp;
  1210.  
  1211.     for (len = col; len > 0 && isidchar(ptr[len - 1]); --len)
  1212.         ;
  1213.     if (len < mincol)
  1214.         len = mincol;
  1215.     if (len < col)                /* there is a word in front of the cursor */
  1216.     {
  1217.         ptr += len;
  1218.         len = col - len;
  1219.         for (mp = maplist.m_next; mp; mp = mp->m_next)
  1220.         {
  1221.                     /* find entries with right mode and keys */
  1222.             if ((mp->m_mode & ABBREV) == ABBREV &&
  1223.                         (mp->m_mode & State) &&
  1224.                         mp->m_keylen == len &&
  1225.                         !strncmp(mp->m_keys, ptr, (size_t)len))
  1226.                 break;
  1227.         }
  1228.         if (mp)                                /* found a match */
  1229.         {
  1230.             j = 0;
  1231.             if (c < 0x100 && (c < ' ' || c > '~'))
  1232.                 tb[j++] = Ctrl('V');        /* special char needs CTRL-V */
  1233.             tb[j++] = c;
  1234.             tb[j] = NUL;
  1235.             ins_typestr(tb, TRUE);            /* insert the last typed char */
  1236.             ins_typestr(mp->m_str, mp->m_noremap);    /* insert the to string */
  1237.             while (len--)
  1238.                 ins_typestr("\b", TRUE);    /* delete the from string */
  1239.             return TRUE;
  1240.         }
  1241.     }
  1242.     return FALSE;
  1243. }
  1244.  
  1245. /*
  1246.  * Write map commands for the current mappings to an .exrc file.
  1247.  * Return 1 on error.
  1248.  */
  1249.     int
  1250. makemap(fd)
  1251.     FILE *fd;
  1252. {
  1253.     struct mapblock *mp;
  1254.     char            c1;
  1255.     char             *p;
  1256.  
  1257.     for (mp = maplist.m_next; mp; mp = mp->m_next)
  1258.     {
  1259.         c1 = NUL;
  1260.         p = "map";
  1261.         switch (mp->m_mode)
  1262.         {
  1263.         case NORMAL:
  1264.             break;
  1265.         case CMDLINE + INSERT:
  1266.             p = "map!";
  1267.             break;
  1268.         case CMDLINE:
  1269.             c1 = 'c';
  1270.             break;
  1271.         case INSERT:
  1272.             c1 = 'i';
  1273.             break;
  1274.         case INSERT + CMDLINE + ABBREV:
  1275.             p = "abbr";
  1276.             break;
  1277.         case CMDLINE + ABBREV:
  1278.             c1 = 'c';
  1279.             p = "abbr";
  1280.             break;
  1281.         case INSERT + ABBREV:
  1282.             c1 = 'i';
  1283.             p = "abbr";
  1284.             break;
  1285.         default:
  1286.             emsg("makemap: Illegal mode");
  1287.             return 1;
  1288.         }
  1289.         if (c1 && putc(c1, fd) < 0)
  1290.             return 1;
  1291.         if (mp->m_noremap && fprintf(fd, "nore") < 0)
  1292.             return 1;
  1293.         if (fprintf(fd, p) < 0)
  1294.             return 1;
  1295.  
  1296.         if (    putc(' ', fd) < 0 || putescstr(fd, mp->m_keys, FALSE) < 0 ||
  1297.                 putc(' ', fd) < 0 || putescstr(fd, mp->m_str, FALSE) < 0 ||
  1298. #ifdef MSDOS
  1299.                 putc('\r', fd) < 0 ||
  1300. #endif
  1301.                 putc('\n', fd) < 0)
  1302.             return 1;
  1303.     }
  1304.     return 0;
  1305. }
  1306.  
  1307.     int
  1308. putescstr(fd, str, set)
  1309.     FILE        *fd;
  1310.     char        *str;
  1311.     int            set;        /* TRUE for makeset, FALSE for makemap */
  1312. {
  1313.     for ( ; *str; ++str)
  1314.     {
  1315.         /*
  1316.          * some characters have to be escaped with CTRL-V to
  1317.          * prevent them from misinterpreted in DoOneCmd().
  1318.          * A space has to be escaped with a backslash to
  1319.          * prevent it to be misinterpreted in doset().
  1320.          */
  1321.         if (*str < ' ' || *str > '~' || (*str == ' ' && !set))
  1322.         {
  1323.             if (putc(Ctrl('V'), fd) < 0)
  1324.                 return -1;
  1325.         }
  1326.         else if ((set && *str == ' ') || *str == '|')
  1327.         {
  1328.             if (putc('\\', fd) < 0)
  1329.                 return -1;
  1330.         }
  1331.         if (putc(*str, fd) < 0)
  1332.             return -1;
  1333.     }
  1334.     return 0;
  1335. }
  1336.