xref: /vim-8.2.3635/src/map.c (revision 4a15504e)
1b66bab38SBram Moolenaar /* vi:set ts=8 sts=4 sw=4 noet:
2b66bab38SBram Moolenaar  *
3b66bab38SBram Moolenaar  * VIM - Vi IMproved	by Bram Moolenaar
4b66bab38SBram Moolenaar  *
5b66bab38SBram Moolenaar  * Do ":help uganda"  in Vim to read copying and usage conditions.
6b66bab38SBram Moolenaar  * Do ":help credits" in Vim to see a list of people who contributed.
7b66bab38SBram Moolenaar  * See README.txt for an overview of the Vim source code.
8b66bab38SBram Moolenaar  */
9b66bab38SBram Moolenaar 
10b66bab38SBram Moolenaar /*
11b66bab38SBram Moolenaar  * map.c: functions for maps and abbreviations
12b66bab38SBram Moolenaar  */
13b66bab38SBram Moolenaar 
14b66bab38SBram Moolenaar #include "vim.h"
15b66bab38SBram Moolenaar 
16b66bab38SBram Moolenaar /*
17b66bab38SBram Moolenaar  * List used for abbreviations.
18b66bab38SBram Moolenaar  */
19b66bab38SBram Moolenaar static mapblock_T	*first_abbr = NULL; // first entry in abbrlist
20b66bab38SBram Moolenaar 
21b66bab38SBram Moolenaar /*
22b66bab38SBram Moolenaar  * Each mapping is put in one of the 256 hash lists, to speed up finding it.
23b66bab38SBram Moolenaar  */
24b66bab38SBram Moolenaar static mapblock_T	*(maphash[256]);
25b66bab38SBram Moolenaar static int		maphash_valid = FALSE;
26b66bab38SBram Moolenaar 
27b66bab38SBram Moolenaar /*
28b66bab38SBram Moolenaar  * Make a hash value for a mapping.
29b66bab38SBram Moolenaar  * "mode" is the lower 4 bits of the State for the mapping.
30b66bab38SBram Moolenaar  * "c1" is the first character of the "lhs".
31b66bab38SBram Moolenaar  * Returns a value between 0 and 255, index in maphash.
32b66bab38SBram Moolenaar  * Put Normal/Visual mode mappings mostly separately from Insert/Cmdline mode.
33b66bab38SBram Moolenaar  */
34b66bab38SBram Moolenaar #define MAP_HASH(mode, c1) (((mode) & (NORMAL + VISUAL + SELECTMODE + OP_PENDING + TERMINAL)) ? (c1) : ((c1) ^ 0x80))
35b66bab38SBram Moolenaar 
36b66bab38SBram Moolenaar /*
37b66bab38SBram Moolenaar  * Get the start of the hashed map list for "state" and first character "c".
38b66bab38SBram Moolenaar  */
39b66bab38SBram Moolenaar     mapblock_T *
get_maphash_list(int state,int c)40b66bab38SBram Moolenaar get_maphash_list(int state, int c)
41b66bab38SBram Moolenaar {
42b66bab38SBram Moolenaar     return maphash[MAP_HASH(state, c)];
43b66bab38SBram Moolenaar }
44b66bab38SBram Moolenaar 
45b66bab38SBram Moolenaar /*
46b66bab38SBram Moolenaar  * Get the buffer-local hashed map list for "state" and first character "c".
47b66bab38SBram Moolenaar  */
48b66bab38SBram Moolenaar     mapblock_T *
get_buf_maphash_list(int state,int c)49b66bab38SBram Moolenaar get_buf_maphash_list(int state, int c)
50b66bab38SBram Moolenaar {
51b66bab38SBram Moolenaar     return curbuf->b_maphash[MAP_HASH(state, c)];
52b66bab38SBram Moolenaar }
53b66bab38SBram Moolenaar 
54b66bab38SBram Moolenaar     int
is_maphash_valid(void)55b66bab38SBram Moolenaar is_maphash_valid(void)
56b66bab38SBram Moolenaar {
57b66bab38SBram Moolenaar     return maphash_valid;
58b66bab38SBram Moolenaar }
59b66bab38SBram Moolenaar 
60b66bab38SBram Moolenaar /*
61b66bab38SBram Moolenaar  * Initialize maphash[] for first use.
62b66bab38SBram Moolenaar  */
63b66bab38SBram Moolenaar     static void
validate_maphash(void)64b66bab38SBram Moolenaar validate_maphash(void)
65b66bab38SBram Moolenaar {
66b66bab38SBram Moolenaar     if (!maphash_valid)
67b66bab38SBram Moolenaar     {
68a80faa89SBram Moolenaar 	CLEAR_FIELD(maphash);
69b66bab38SBram Moolenaar 	maphash_valid = TRUE;
70b66bab38SBram Moolenaar     }
71b66bab38SBram Moolenaar }
72b66bab38SBram Moolenaar 
73b66bab38SBram Moolenaar /*
74b66bab38SBram Moolenaar  * Delete one entry from the abbrlist or maphash[].
75b66bab38SBram Moolenaar  * "mpp" is a pointer to the m_next field of the PREVIOUS entry!
76b66bab38SBram Moolenaar  */
77b66bab38SBram Moolenaar     static void
map_free(mapblock_T ** mpp)78b66bab38SBram Moolenaar map_free(mapblock_T **mpp)
79b66bab38SBram Moolenaar {
80b66bab38SBram Moolenaar     mapblock_T	*mp;
81b66bab38SBram Moolenaar 
82b66bab38SBram Moolenaar     mp = *mpp;
83b66bab38SBram Moolenaar     vim_free(mp->m_keys);
84b66bab38SBram Moolenaar     vim_free(mp->m_str);
85b66bab38SBram Moolenaar     vim_free(mp->m_orig_str);
86b66bab38SBram Moolenaar     *mpp = mp->m_next;
87b66bab38SBram Moolenaar     vim_free(mp);
88b66bab38SBram Moolenaar }
89b66bab38SBram Moolenaar 
90b66bab38SBram Moolenaar /*
91b66bab38SBram Moolenaar  * Return characters to represent the map mode in an allocated string.
92b66bab38SBram Moolenaar  * Returns NULL when out of memory.
93b66bab38SBram Moolenaar  */
94b66bab38SBram Moolenaar     static char_u *
map_mode_to_chars(int mode)95b66bab38SBram Moolenaar map_mode_to_chars(int mode)
96b66bab38SBram Moolenaar {
97b66bab38SBram Moolenaar     garray_T    mapmode;
98b66bab38SBram Moolenaar 
99b66bab38SBram Moolenaar     ga_init2(&mapmode, 1, 7);
100b66bab38SBram Moolenaar 
101b66bab38SBram Moolenaar     if ((mode & (INSERT + CMDLINE)) == INSERT + CMDLINE)
102b66bab38SBram Moolenaar 	ga_append(&mapmode, '!');			// :map!
103b66bab38SBram Moolenaar     else if (mode & INSERT)
104b66bab38SBram Moolenaar 	ga_append(&mapmode, 'i');			// :imap
105b66bab38SBram Moolenaar     else if (mode & LANGMAP)
106b66bab38SBram Moolenaar 	ga_append(&mapmode, 'l');			// :lmap
107b66bab38SBram Moolenaar     else if (mode & CMDLINE)
108b66bab38SBram Moolenaar 	ga_append(&mapmode, 'c');			// :cmap
109b66bab38SBram Moolenaar     else if ((mode & (NORMAL + VISUAL + SELECTMODE + OP_PENDING))
110b66bab38SBram Moolenaar 				 == NORMAL + VISUAL + SELECTMODE + OP_PENDING)
111b66bab38SBram Moolenaar 	ga_append(&mapmode, ' ');			// :map
112b66bab38SBram Moolenaar     else
113b66bab38SBram Moolenaar     {
114b66bab38SBram Moolenaar 	if (mode & NORMAL)
115b66bab38SBram Moolenaar 	    ga_append(&mapmode, 'n');			// :nmap
116b66bab38SBram Moolenaar 	if (mode & OP_PENDING)
117b66bab38SBram Moolenaar 	    ga_append(&mapmode, 'o');			// :omap
118b66bab38SBram Moolenaar 	if (mode & TERMINAL)
119b66bab38SBram Moolenaar 	    ga_append(&mapmode, 't');			// :tmap
120b66bab38SBram Moolenaar 	if ((mode & (VISUAL + SELECTMODE)) == VISUAL + SELECTMODE)
121b66bab38SBram Moolenaar 	    ga_append(&mapmode, 'v');			// :vmap
122b66bab38SBram Moolenaar 	else
123b66bab38SBram Moolenaar 	{
124b66bab38SBram Moolenaar 	    if (mode & VISUAL)
125b66bab38SBram Moolenaar 		ga_append(&mapmode, 'x');		// :xmap
126b66bab38SBram Moolenaar 	    if (mode & SELECTMODE)
127b66bab38SBram Moolenaar 		ga_append(&mapmode, 's');		// :smap
128b66bab38SBram Moolenaar 	}
129b66bab38SBram Moolenaar     }
130b66bab38SBram Moolenaar 
131b66bab38SBram Moolenaar     ga_append(&mapmode, NUL);
132b66bab38SBram Moolenaar     return (char_u *)mapmode.ga_data;
133b66bab38SBram Moolenaar }
134b66bab38SBram Moolenaar 
135b66bab38SBram Moolenaar     static void
showmap(mapblock_T * mp,int local)136b66bab38SBram Moolenaar showmap(
137b66bab38SBram Moolenaar     mapblock_T	*mp,
138b66bab38SBram Moolenaar     int		local)	    // TRUE for buffer-local map
139b66bab38SBram Moolenaar {
140b66bab38SBram Moolenaar     int		len = 1;
141b66bab38SBram Moolenaar     char_u	*mapchars;
142b66bab38SBram Moolenaar 
143b66bab38SBram Moolenaar     if (message_filtered(mp->m_keys) && message_filtered(mp->m_str))
144b66bab38SBram Moolenaar 	return;
145b66bab38SBram Moolenaar 
146b66bab38SBram Moolenaar     if (msg_didout || msg_silent != 0)
147b66bab38SBram Moolenaar     {
148b66bab38SBram Moolenaar 	msg_putchar('\n');
149b66bab38SBram Moolenaar 	if (got_int)	    // 'q' typed at MORE prompt
150b66bab38SBram Moolenaar 	    return;
151b66bab38SBram Moolenaar     }
152b66bab38SBram Moolenaar 
153b66bab38SBram Moolenaar     mapchars = map_mode_to_chars(mp->m_mode);
154b66bab38SBram Moolenaar     if (mapchars != NULL)
155b66bab38SBram Moolenaar     {
156b66bab38SBram Moolenaar 	msg_puts((char *)mapchars);
157b66bab38SBram Moolenaar 	len = (int)STRLEN(mapchars);
158b66bab38SBram Moolenaar 	vim_free(mapchars);
159b66bab38SBram Moolenaar     }
160b66bab38SBram Moolenaar 
161b66bab38SBram Moolenaar     while (++len <= 3)
162b66bab38SBram Moolenaar 	msg_putchar(' ');
163b66bab38SBram Moolenaar 
164b66bab38SBram Moolenaar     // Display the LHS.  Get length of what we write.
165b66bab38SBram Moolenaar     len = msg_outtrans_special(mp->m_keys, TRUE, 0);
166b66bab38SBram Moolenaar     do
167b66bab38SBram Moolenaar     {
168b66bab38SBram Moolenaar 	msg_putchar(' ');		// padd with blanks
169b66bab38SBram Moolenaar 	++len;
170b66bab38SBram Moolenaar     } while (len < 12);
171b66bab38SBram Moolenaar 
172b66bab38SBram Moolenaar     if (mp->m_noremap == REMAP_NONE)
173b66bab38SBram Moolenaar 	msg_puts_attr("*", HL_ATTR(HLF_8));
174b66bab38SBram Moolenaar     else if (mp->m_noremap == REMAP_SCRIPT)
175b66bab38SBram Moolenaar 	msg_puts_attr("&", HL_ATTR(HLF_8));
176b66bab38SBram Moolenaar     else
177b66bab38SBram Moolenaar 	msg_putchar(' ');
178b66bab38SBram Moolenaar 
179b66bab38SBram Moolenaar     if (local)
180b66bab38SBram Moolenaar 	msg_putchar('@');
181b66bab38SBram Moolenaar     else
182b66bab38SBram Moolenaar 	msg_putchar(' ');
183b66bab38SBram Moolenaar 
184b66bab38SBram Moolenaar     // Use FALSE below if we only want things like <Up> to show up as such on
185b66bab38SBram Moolenaar     // the rhs, and not M-x etc, TRUE gets both -- webb
186b66bab38SBram Moolenaar     if (*mp->m_str == NUL)
187b66bab38SBram Moolenaar 	msg_puts_attr("<Nop>", HL_ATTR(HLF_8));
188b66bab38SBram Moolenaar     else
189b66bab38SBram Moolenaar     {
190b66bab38SBram Moolenaar 	// Remove escaping of CSI, because "m_str" is in a format to be used
191b66bab38SBram Moolenaar 	// as typeahead.
192b66bab38SBram Moolenaar 	char_u *s = vim_strsave(mp->m_str);
193b66bab38SBram Moolenaar 	if (s != NULL)
194b66bab38SBram Moolenaar 	{
195b66bab38SBram Moolenaar 	    vim_unescape_csi(s);
196b66bab38SBram Moolenaar 	    msg_outtrans_special(s, FALSE, 0);
197b66bab38SBram Moolenaar 	    vim_free(s);
198b66bab38SBram Moolenaar 	}
199b66bab38SBram Moolenaar     }
200b66bab38SBram Moolenaar #ifdef FEAT_EVAL
201b66bab38SBram Moolenaar     if (p_verbose > 0)
202b66bab38SBram Moolenaar 	last_set_msg(mp->m_script_ctx);
203b66bab38SBram Moolenaar #endif
204b66bab38SBram Moolenaar     out_flush();			// show one line at a time
205b66bab38SBram Moolenaar }
206b66bab38SBram Moolenaar 
2074c9243f9SBram Moolenaar     static int
map_add(mapblock_T ** map_table,mapblock_T ** abbr_table,char_u * keys,char_u * rhs,char_u * orig_rhs,int noremap,int nowait,int silent,int mode,int is_abbr,int expr,scid_T sid,linenr_T lnum,int simplified)2084c9243f9SBram Moolenaar map_add(
2094c9243f9SBram Moolenaar 	mapblock_T  **map_table,
2104c9243f9SBram Moolenaar 	mapblock_T  **abbr_table,
2114c9243f9SBram Moolenaar 	char_u	    *keys,
2124c9243f9SBram Moolenaar 	char_u	    *rhs,
2134c9243f9SBram Moolenaar 	char_u	    *orig_rhs,
2144c9243f9SBram Moolenaar 	int	    noremap,
2154c9243f9SBram Moolenaar 	int	    nowait,
2164c9243f9SBram Moolenaar 	int	    silent,
2174c9243f9SBram Moolenaar 	int	    mode,
2184c9243f9SBram Moolenaar 	int	    is_abbr,
2194c9243f9SBram Moolenaar #ifdef FEAT_EVAL
2205a80f8adSBram Moolenaar 	int	    expr,
2214c9243f9SBram Moolenaar 	scid_T	    sid,	    // -1 to use current_sctx
2224c9243f9SBram Moolenaar 	linenr_T    lnum,
2234c9243f9SBram Moolenaar #endif
2244c9243f9SBram Moolenaar 	int	    simplified)
2254c9243f9SBram Moolenaar {
2264c9243f9SBram Moolenaar     mapblock_T	*mp = ALLOC_ONE(mapblock_T);
2274c9243f9SBram Moolenaar 
2284c9243f9SBram Moolenaar     if (mp == NULL)
2294c9243f9SBram Moolenaar 	return FAIL;
2304c9243f9SBram Moolenaar 
2314c9243f9SBram Moolenaar     // If CTRL-C has been mapped, don't always use it for Interrupting.
2324c9243f9SBram Moolenaar     if (*keys == Ctrl_C)
2334c9243f9SBram Moolenaar     {
2344c9243f9SBram Moolenaar 	if (map_table == curbuf->b_maphash)
2354c9243f9SBram Moolenaar 	    curbuf->b_mapped_ctrl_c |= mode;
2364c9243f9SBram Moolenaar 	else
2374c9243f9SBram Moolenaar 	    mapped_ctrl_c |= mode;
2384c9243f9SBram Moolenaar     }
2394c9243f9SBram Moolenaar 
2404c9243f9SBram Moolenaar     mp->m_keys = vim_strsave(keys);
2414c9243f9SBram Moolenaar     mp->m_str = vim_strsave(rhs);
2424c9243f9SBram Moolenaar     mp->m_orig_str = vim_strsave(orig_rhs);
2434c9243f9SBram Moolenaar     if (mp->m_keys == NULL || mp->m_str == NULL)
2444c9243f9SBram Moolenaar     {
2454c9243f9SBram Moolenaar 	vim_free(mp->m_keys);
2464c9243f9SBram Moolenaar 	vim_free(mp->m_str);
2474c9243f9SBram Moolenaar 	vim_free(mp->m_orig_str);
2484c9243f9SBram Moolenaar 	vim_free(mp);
2494c9243f9SBram Moolenaar 	return FAIL;
2504c9243f9SBram Moolenaar     }
2514c9243f9SBram Moolenaar     mp->m_keylen = (int)STRLEN(mp->m_keys);
2524c9243f9SBram Moolenaar     mp->m_noremap = noremap;
2534c9243f9SBram Moolenaar     mp->m_nowait = nowait;
2544c9243f9SBram Moolenaar     mp->m_silent = silent;
2554c9243f9SBram Moolenaar     mp->m_mode = mode;
2564c9243f9SBram Moolenaar     mp->m_simplified = simplified;
2574c9243f9SBram Moolenaar #ifdef FEAT_EVAL
2584c9243f9SBram Moolenaar     mp->m_expr = expr;
2594c9243f9SBram Moolenaar     if (sid >= 0)
2604c9243f9SBram Moolenaar     {
2614c9243f9SBram Moolenaar 	mp->m_script_ctx.sc_sid = sid;
2624c9243f9SBram Moolenaar 	mp->m_script_ctx.sc_lnum = lnum;
2634c9243f9SBram Moolenaar     }
2644c9243f9SBram Moolenaar     else
2654c9243f9SBram Moolenaar     {
2664c9243f9SBram Moolenaar 	mp->m_script_ctx = current_sctx;
2674c9243f9SBram Moolenaar 	mp->m_script_ctx.sc_lnum += SOURCING_LNUM;
2684c9243f9SBram Moolenaar     }
2694c9243f9SBram Moolenaar #endif
2704c9243f9SBram Moolenaar 
2714c9243f9SBram Moolenaar     // add the new entry in front of the abbrlist or maphash[] list
2724c9243f9SBram Moolenaar     if (is_abbr)
2734c9243f9SBram Moolenaar     {
2744c9243f9SBram Moolenaar 	mp->m_next = *abbr_table;
2754c9243f9SBram Moolenaar 	*abbr_table = mp;
2764c9243f9SBram Moolenaar     }
2774c9243f9SBram Moolenaar     else
2784c9243f9SBram Moolenaar     {
2794c9243f9SBram Moolenaar 	int n = MAP_HASH(mp->m_mode, mp->m_keys[0]);
2804c9243f9SBram Moolenaar 
2814c9243f9SBram Moolenaar 	mp->m_next = map_table[n];
2824c9243f9SBram Moolenaar 	map_table[n] = mp;
2834c9243f9SBram Moolenaar     }
2844c9243f9SBram Moolenaar     return OK;
2854c9243f9SBram Moolenaar }
2864c9243f9SBram Moolenaar 
287b66bab38SBram Moolenaar /*
288b66bab38SBram Moolenaar  * map[!]		    : show all key mappings
289b66bab38SBram Moolenaar  * map[!] {lhs}		    : show key mapping for {lhs}
290b66bab38SBram Moolenaar  * map[!] {lhs} {rhs}	    : set key mapping for {lhs} to {rhs}
291b66bab38SBram Moolenaar  * noremap[!] {lhs} {rhs}   : same, but no remapping for {rhs}
292b66bab38SBram Moolenaar  * unmap[!] {lhs}	    : remove key mapping for {lhs}
293b66bab38SBram Moolenaar  * abbr			    : show all abbreviations
294b66bab38SBram Moolenaar  * abbr {lhs}		    : show abbreviations for {lhs}
295b66bab38SBram Moolenaar  * abbr {lhs} {rhs}	    : set abbreviation for {lhs} to {rhs}
296b66bab38SBram Moolenaar  * noreabbr {lhs} {rhs}	    : same, but no remapping for {rhs}
297b66bab38SBram Moolenaar  * unabbr {lhs}		    : remove abbreviation for {lhs}
298b66bab38SBram Moolenaar  *
299b66bab38SBram Moolenaar  * maptype: 0 for :map, 1 for :unmap, 2 for noremap.
300b66bab38SBram Moolenaar  *
301b66bab38SBram Moolenaar  * arg is pointer to any arguments. Note: arg cannot be a read-only string,
302b66bab38SBram Moolenaar  * it will be modified.
303b66bab38SBram Moolenaar  *
304b66bab38SBram Moolenaar  * for :map   mode is NORMAL + VISUAL + SELECTMODE + OP_PENDING
305b66bab38SBram Moolenaar  * for :map!  mode is INSERT + CMDLINE
306b66bab38SBram Moolenaar  * for :cmap  mode is CMDLINE
307b66bab38SBram Moolenaar  * for :imap  mode is INSERT
308b66bab38SBram Moolenaar  * for :lmap  mode is LANGMAP
309b66bab38SBram Moolenaar  * for :nmap  mode is NORMAL
310b66bab38SBram Moolenaar  * for :vmap  mode is VISUAL + SELECTMODE
311b66bab38SBram Moolenaar  * for :xmap  mode is VISUAL
312b66bab38SBram Moolenaar  * for :smap  mode is SELECTMODE
313b66bab38SBram Moolenaar  * for :omap  mode is OP_PENDING
314b66bab38SBram Moolenaar  * for :tmap  mode is TERMINAL
315b66bab38SBram Moolenaar  *
316b66bab38SBram Moolenaar  * for :abbr  mode is INSERT + CMDLINE
317b66bab38SBram Moolenaar  * for :iabbr mode is INSERT
318b66bab38SBram Moolenaar  * for :cabbr mode is CMDLINE
319b66bab38SBram Moolenaar  *
320b66bab38SBram Moolenaar  * Return 0 for success
321b66bab38SBram Moolenaar  *	  1 for invalid arguments
322b66bab38SBram Moolenaar  *	  2 for no match
323b66bab38SBram Moolenaar  *	  4 for out of mem
324b66bab38SBram Moolenaar  *	  5 for entry not unique
325b66bab38SBram Moolenaar  */
326b66bab38SBram Moolenaar     int
do_map(int maptype,char_u * arg,int mode,int abbrev)327b66bab38SBram Moolenaar do_map(
328b66bab38SBram Moolenaar     int		maptype,
329b66bab38SBram Moolenaar     char_u	*arg,
330b66bab38SBram Moolenaar     int		mode,
331b66bab38SBram Moolenaar     int		abbrev)		// not a mapping but an abbreviation
332b66bab38SBram Moolenaar {
333b66bab38SBram Moolenaar     char_u	*keys;
334b66bab38SBram Moolenaar     mapblock_T	*mp, **mpp;
335b66bab38SBram Moolenaar     char_u	*rhs;
336b66bab38SBram Moolenaar     char_u	*p;
337b66bab38SBram Moolenaar     int		n;
338b66bab38SBram Moolenaar     int		len = 0;	// init for GCC
339b66bab38SBram Moolenaar     int		hasarg;
340b66bab38SBram Moolenaar     int		haskey;
341459fd785SBram Moolenaar     int		do_print;
342459fd785SBram Moolenaar     int		keyround;
343b66bab38SBram Moolenaar     char_u	*keys_buf = NULL;
344459fd785SBram Moolenaar     char_u	*alt_keys_buf = NULL;
345b66bab38SBram Moolenaar     char_u	*arg_buf = NULL;
346b66bab38SBram Moolenaar     int		retval = 0;
347b66bab38SBram Moolenaar     int		do_backslash;
348b66bab38SBram Moolenaar     mapblock_T	**abbr_table;
349b66bab38SBram Moolenaar     mapblock_T	**map_table;
350b66bab38SBram Moolenaar     int		unique = FALSE;
351b66bab38SBram Moolenaar     int		nowait = FALSE;
352b66bab38SBram Moolenaar     int		silent = FALSE;
353b66bab38SBram Moolenaar     int		special = FALSE;
354b66bab38SBram Moolenaar #ifdef FEAT_EVAL
355b66bab38SBram Moolenaar     int		expr = FALSE;
356b66bab38SBram Moolenaar #endif
357459fd785SBram Moolenaar     int		did_simplify = FALSE;
358b66bab38SBram Moolenaar     int		noremap;
359b66bab38SBram Moolenaar     char_u      *orig_rhs;
360b66bab38SBram Moolenaar 
361b66bab38SBram Moolenaar     keys = arg;
362b66bab38SBram Moolenaar     map_table = maphash;
363b66bab38SBram Moolenaar     abbr_table = &first_abbr;
364b66bab38SBram Moolenaar 
365b66bab38SBram Moolenaar     // For ":noremap" don't remap, otherwise do remap.
366b66bab38SBram Moolenaar     if (maptype == 2)
367b66bab38SBram Moolenaar 	noremap = REMAP_NONE;
368b66bab38SBram Moolenaar     else
369b66bab38SBram Moolenaar 	noremap = REMAP_YES;
370b66bab38SBram Moolenaar 
371b66bab38SBram Moolenaar     // Accept <buffer>, <nowait>, <silent>, <expr> <script> and <unique> in
372b66bab38SBram Moolenaar     // any order.
373b66bab38SBram Moolenaar     for (;;)
374b66bab38SBram Moolenaar     {
375b66bab38SBram Moolenaar 	// Check for "<buffer>": mapping local to buffer.
376b66bab38SBram Moolenaar 	if (STRNCMP(keys, "<buffer>", 8) == 0)
377b66bab38SBram Moolenaar 	{
378b66bab38SBram Moolenaar 	    keys = skipwhite(keys + 8);
379b66bab38SBram Moolenaar 	    map_table = curbuf->b_maphash;
380b66bab38SBram Moolenaar 	    abbr_table = &curbuf->b_first_abbr;
381b66bab38SBram Moolenaar 	    continue;
382b66bab38SBram Moolenaar 	}
383b66bab38SBram Moolenaar 
384b66bab38SBram Moolenaar 	// Check for "<nowait>": don't wait for more characters.
385b66bab38SBram Moolenaar 	if (STRNCMP(keys, "<nowait>", 8) == 0)
386b66bab38SBram Moolenaar 	{
387b66bab38SBram Moolenaar 	    keys = skipwhite(keys + 8);
388b66bab38SBram Moolenaar 	    nowait = TRUE;
389b66bab38SBram Moolenaar 	    continue;
390b66bab38SBram Moolenaar 	}
391b66bab38SBram Moolenaar 
392b66bab38SBram Moolenaar 	// Check for "<silent>": don't echo commands.
393b66bab38SBram Moolenaar 	if (STRNCMP(keys, "<silent>", 8) == 0)
394b66bab38SBram Moolenaar 	{
395b66bab38SBram Moolenaar 	    keys = skipwhite(keys + 8);
396b66bab38SBram Moolenaar 	    silent = TRUE;
397b66bab38SBram Moolenaar 	    continue;
398b66bab38SBram Moolenaar 	}
399b66bab38SBram Moolenaar 
400b66bab38SBram Moolenaar 	// Check for "<special>": accept special keys in <>
401b66bab38SBram Moolenaar 	if (STRNCMP(keys, "<special>", 9) == 0)
402b66bab38SBram Moolenaar 	{
403b66bab38SBram Moolenaar 	    keys = skipwhite(keys + 9);
404b66bab38SBram Moolenaar 	    special = TRUE;
405b66bab38SBram Moolenaar 	    continue;
406b66bab38SBram Moolenaar 	}
407b66bab38SBram Moolenaar 
408b66bab38SBram Moolenaar #ifdef FEAT_EVAL
409b66bab38SBram Moolenaar 	// Check for "<script>": remap script-local mappings only
410b66bab38SBram Moolenaar 	if (STRNCMP(keys, "<script>", 8) == 0)
411b66bab38SBram Moolenaar 	{
412b66bab38SBram Moolenaar 	    keys = skipwhite(keys + 8);
413b66bab38SBram Moolenaar 	    noremap = REMAP_SCRIPT;
414b66bab38SBram Moolenaar 	    continue;
415b66bab38SBram Moolenaar 	}
416b66bab38SBram Moolenaar 
417b66bab38SBram Moolenaar 	// Check for "<expr>": {rhs} is an expression.
418b66bab38SBram Moolenaar 	if (STRNCMP(keys, "<expr>", 6) == 0)
419b66bab38SBram Moolenaar 	{
420b66bab38SBram Moolenaar 	    keys = skipwhite(keys + 6);
421b66bab38SBram Moolenaar 	    expr = TRUE;
422b66bab38SBram Moolenaar 	    continue;
423b66bab38SBram Moolenaar 	}
424b66bab38SBram Moolenaar #endif
425b66bab38SBram Moolenaar 	// Check for "<unique>": don't overwrite an existing mapping.
426b66bab38SBram Moolenaar 	if (STRNCMP(keys, "<unique>", 8) == 0)
427b66bab38SBram Moolenaar 	{
428b66bab38SBram Moolenaar 	    keys = skipwhite(keys + 8);
429b66bab38SBram Moolenaar 	    unique = TRUE;
430b66bab38SBram Moolenaar 	    continue;
431b66bab38SBram Moolenaar 	}
432b66bab38SBram Moolenaar 	break;
433b66bab38SBram Moolenaar     }
434b66bab38SBram Moolenaar 
435b66bab38SBram Moolenaar     validate_maphash();
436b66bab38SBram Moolenaar 
437b66bab38SBram Moolenaar     // Find end of keys and skip CTRL-Vs (and backslashes) in it.
438b66bab38SBram Moolenaar     // Accept backslash like CTRL-V when 'cpoptions' does not contain 'B'.
439b66bab38SBram Moolenaar     // with :unmap white space is included in the keys, no argument possible.
440b66bab38SBram Moolenaar     p = keys;
441b66bab38SBram Moolenaar     do_backslash = (vim_strchr(p_cpo, CPO_BSLASH) == NULL);
442b66bab38SBram Moolenaar     while (*p && (maptype == 1 || !VIM_ISWHITE(*p)))
443b66bab38SBram Moolenaar     {
444b66bab38SBram Moolenaar 	if ((p[0] == Ctrl_V || (do_backslash && p[0] == '\\')) &&
445b66bab38SBram Moolenaar 								  p[1] != NUL)
446b66bab38SBram Moolenaar 	    ++p;		// skip CTRL-V or backslash
447b66bab38SBram Moolenaar 	++p;
448b66bab38SBram Moolenaar     }
449b66bab38SBram Moolenaar     if (*p != NUL)
450b66bab38SBram Moolenaar 	*p++ = NUL;
451b66bab38SBram Moolenaar 
452b66bab38SBram Moolenaar     p = skipwhite(p);
453b66bab38SBram Moolenaar     rhs = p;
454b66bab38SBram Moolenaar     hasarg = (*rhs != NUL);
455b66bab38SBram Moolenaar     haskey = (*keys != NUL);
456459fd785SBram Moolenaar     do_print = !haskey || (maptype != 1 && !hasarg);
457b66bab38SBram Moolenaar 
458b66bab38SBram Moolenaar     // check for :unmap without argument
459b66bab38SBram Moolenaar     if (maptype == 1 && !haskey)
460b66bab38SBram Moolenaar     {
461b66bab38SBram Moolenaar 	retval = 1;
462b66bab38SBram Moolenaar 	goto theend;
463b66bab38SBram Moolenaar     }
464b66bab38SBram Moolenaar 
465b66bab38SBram Moolenaar     // If mapping has been given as ^V<C_UP> say, then replace the term codes
466b66bab38SBram Moolenaar     // with the appropriate two bytes. If it is a shifted special key, unshift
467b66bab38SBram Moolenaar     // it too, giving another two bytes.
468b66bab38SBram Moolenaar     // replace_termcodes() may move the result to allocated memory, which
469b66bab38SBram Moolenaar     // needs to be freed later (*keys_buf and *arg_buf).
470b66bab38SBram Moolenaar     // replace_termcodes() also removes CTRL-Vs and sometimes backslashes.
471459fd785SBram Moolenaar     // If something like <C-H> is simplified to 0x08 then mark it as simplified
472459fd785SBram Moolenaar     // and also add a n entry with a modifier, which will work when
473459fd785SBram Moolenaar     // modifyOtherKeys is working.
474b66bab38SBram Moolenaar     if (haskey)
475459fd785SBram Moolenaar     {
476459fd785SBram Moolenaar 	char_u	*new_keys;
477459fd785SBram Moolenaar 	int	flags = REPTERM_FROM_PART | REPTERM_DO_LT;
478459fd785SBram Moolenaar 
479459fd785SBram Moolenaar 	if (special)
480459fd785SBram Moolenaar 	    flags |= REPTERM_SPECIAL;
481459fd785SBram Moolenaar 	new_keys = replace_termcodes(keys, &keys_buf, flags, &did_simplify);
482459fd785SBram Moolenaar 	if (did_simplify)
483459fd785SBram Moolenaar 	    (void)replace_termcodes(keys, &alt_keys_buf,
484459fd785SBram Moolenaar 					    flags | REPTERM_NO_SIMPLIFY, NULL);
485459fd785SBram Moolenaar 	keys = new_keys;
486459fd785SBram Moolenaar     }
487b66bab38SBram Moolenaar     orig_rhs = rhs;
488b66bab38SBram Moolenaar     if (hasarg)
489b66bab38SBram Moolenaar     {
490b66bab38SBram Moolenaar 	if (STRICMP(rhs, "<nop>") == 0)	    // "<Nop>" means nothing
491b66bab38SBram Moolenaar 	    rhs = (char_u *)"";
492b66bab38SBram Moolenaar 	else
493459fd785SBram Moolenaar 	    rhs = replace_termcodes(rhs, &arg_buf,
494459fd785SBram Moolenaar 			REPTERM_DO_LT | (special ? REPTERM_SPECIAL : 0), NULL);
495b66bab38SBram Moolenaar     }
496b66bab38SBram Moolenaar 
497459fd785SBram Moolenaar     /*
498459fd785SBram Moolenaar      * The following is done twice if we have two versions of keys:
499459fd785SBram Moolenaar      * "alt_keys_buf" is not NULL.
500459fd785SBram Moolenaar      */
501459fd785SBram Moolenaar     for (keyround = 1; keyround <= 2; ++keyround)
502459fd785SBram Moolenaar     {
503459fd785SBram Moolenaar 	int	did_it = FALSE;
504459fd785SBram Moolenaar 	int	did_local = FALSE;
505459fd785SBram Moolenaar 	int	round;
506459fd785SBram Moolenaar 	int	hash;
507459fd785SBram Moolenaar 	int	new_hash;
508459fd785SBram Moolenaar 
509459fd785SBram Moolenaar 	if (keyround == 2)
510459fd785SBram Moolenaar 	{
511459fd785SBram Moolenaar 	    if (alt_keys_buf == NULL)
512459fd785SBram Moolenaar 		break;
513459fd785SBram Moolenaar 	    keys = alt_keys_buf;
514459fd785SBram Moolenaar 	}
515459fd785SBram Moolenaar 	else if (alt_keys_buf != NULL && do_print)
516459fd785SBram Moolenaar 	    // when printing always use the not-simplified map
517459fd785SBram Moolenaar 	    keys = alt_keys_buf;
518459fd785SBram Moolenaar 
519b66bab38SBram Moolenaar 	// check arguments and translate function keys
520b66bab38SBram Moolenaar 	if (haskey)
521b66bab38SBram Moolenaar 	{
522b66bab38SBram Moolenaar 	    len = (int)STRLEN(keys);
523b66bab38SBram Moolenaar 	    if (len > MAXMAPLEN)	// maximum length of MAXMAPLEN chars
524b66bab38SBram Moolenaar 	    {
525b66bab38SBram Moolenaar 		retval = 1;
526b66bab38SBram Moolenaar 		goto theend;
527b66bab38SBram Moolenaar 	    }
528b66bab38SBram Moolenaar 
529b66bab38SBram Moolenaar 	    if (abbrev && maptype != 1)
530b66bab38SBram Moolenaar 	    {
531b66bab38SBram Moolenaar 		// If an abbreviation ends in a keyword character, the
532b66bab38SBram Moolenaar 		// rest must be all keyword-char or all non-keyword-char.
533b66bab38SBram Moolenaar 		// Otherwise we won't be able to find the start of it in a
534b66bab38SBram Moolenaar 		// vi-compatible way.
535b66bab38SBram Moolenaar 		if (has_mbyte)
536b66bab38SBram Moolenaar 		{
537b66bab38SBram Moolenaar 		    int	first, last;
538b66bab38SBram Moolenaar 		    int	same = -1;
539b66bab38SBram Moolenaar 
540b66bab38SBram Moolenaar 		    first = vim_iswordp(keys);
541b66bab38SBram Moolenaar 		    last = first;
542b66bab38SBram Moolenaar 		    p = keys + (*mb_ptr2len)(keys);
543b66bab38SBram Moolenaar 		    n = 1;
544b66bab38SBram Moolenaar 		    while (p < keys + len)
545b66bab38SBram Moolenaar 		    {
546b66bab38SBram Moolenaar 			++n;			// nr of (multi-byte) chars
547b66bab38SBram Moolenaar 			last = vim_iswordp(p);	// type of last char
548b66bab38SBram Moolenaar 			if (same == -1 && last != first)
549b66bab38SBram Moolenaar 			    same = n - 1;	// count of same char type
550b66bab38SBram Moolenaar 			p += (*mb_ptr2len)(p);
551b66bab38SBram Moolenaar 		    }
552b66bab38SBram Moolenaar 		    if (last && n > 2 && same >= 0 && same < n - 1)
553b66bab38SBram Moolenaar 		    {
554b66bab38SBram Moolenaar 			retval = 1;
555b66bab38SBram Moolenaar 			goto theend;
556b66bab38SBram Moolenaar 		    }
557b66bab38SBram Moolenaar 		}
558459fd785SBram Moolenaar 		else if (vim_iswordc(keys[len - 1]))
559459fd785SBram Moolenaar 		    // ends in keyword char
560b66bab38SBram Moolenaar 		    for (n = 0; n < len - 2; ++n)
561b66bab38SBram Moolenaar 			if (vim_iswordc(keys[n]) != vim_iswordc(keys[len - 2]))
562b66bab38SBram Moolenaar 			{
563b66bab38SBram Moolenaar 			    retval = 1;
564b66bab38SBram Moolenaar 			    goto theend;
565b66bab38SBram Moolenaar 			}
566b66bab38SBram Moolenaar 		// An abbreviation cannot contain white space.
567b66bab38SBram Moolenaar 		for (n = 0; n < len; ++n)
568b66bab38SBram Moolenaar 		    if (VIM_ISWHITE(keys[n]))
569b66bab38SBram Moolenaar 		    {
570b66bab38SBram Moolenaar 			retval = 1;
571b66bab38SBram Moolenaar 			goto theend;
572b66bab38SBram Moolenaar 		    }
573b66bab38SBram Moolenaar 	    }
574b66bab38SBram Moolenaar 	}
575b66bab38SBram Moolenaar 
576b66bab38SBram Moolenaar 	if (haskey && hasarg && abbrev)	// if we will add an abbreviation
577b66bab38SBram Moolenaar 	    no_abbr = FALSE;		// reset flag that indicates there are
578b66bab38SBram Moolenaar 					// no abbreviations
579b66bab38SBram Moolenaar 
580459fd785SBram Moolenaar 	if (do_print)
581b66bab38SBram Moolenaar 	    msg_start();
582b66bab38SBram Moolenaar 
583b66bab38SBram Moolenaar 	// Check if a new local mapping wasn't already defined globally.
5844c9243f9SBram Moolenaar 	if (unique && map_table == curbuf->b_maphash
5854c9243f9SBram Moolenaar 					   && haskey && hasarg && maptype != 1)
586b66bab38SBram Moolenaar 	{
587b66bab38SBram Moolenaar 	    // need to loop over all global hash lists
588b66bab38SBram Moolenaar 	    for (hash = 0; hash < 256 && !got_int; ++hash)
589b66bab38SBram Moolenaar 	    {
590b66bab38SBram Moolenaar 		if (abbrev)
591b66bab38SBram Moolenaar 		{
592b66bab38SBram Moolenaar 		    if (hash != 0)	// there is only one abbreviation list
593b66bab38SBram Moolenaar 			break;
594b66bab38SBram Moolenaar 		    mp = first_abbr;
595b66bab38SBram Moolenaar 		}
596b66bab38SBram Moolenaar 		else
597b66bab38SBram Moolenaar 		    mp = maphash[hash];
598b66bab38SBram Moolenaar 		for ( ; mp != NULL && !got_int; mp = mp->m_next)
599b66bab38SBram Moolenaar 		{
600b66bab38SBram Moolenaar 		    // check entries with the same mode
601b66bab38SBram Moolenaar 		    if ((mp->m_mode & mode) != 0
602b66bab38SBram Moolenaar 			    && mp->m_keylen == len
603b66bab38SBram Moolenaar 			    && STRNCMP(mp->m_keys, keys, (size_t)len) == 0)
604b66bab38SBram Moolenaar 		    {
605b66bab38SBram Moolenaar 			if (abbrev)
606459fd785SBram Moolenaar 			    semsg(_(
607459fd785SBram Moolenaar 			    "E224: global abbreviation already exists for %s"),
608b66bab38SBram Moolenaar 				    mp->m_keys);
609b66bab38SBram Moolenaar 			else
610459fd785SBram Moolenaar 			    semsg(_(
611459fd785SBram Moolenaar 				 "E225: global mapping already exists for %s"),
612b66bab38SBram Moolenaar 				    mp->m_keys);
613b66bab38SBram Moolenaar 			retval = 5;
614b66bab38SBram Moolenaar 			goto theend;
615b66bab38SBram Moolenaar 		    }
616b66bab38SBram Moolenaar 		}
617b66bab38SBram Moolenaar 	    }
618b66bab38SBram Moolenaar 	}
619b66bab38SBram Moolenaar 
620b66bab38SBram Moolenaar 	// When listing global mappings, also list buffer-local ones here.
621b66bab38SBram Moolenaar 	if (map_table != curbuf->b_maphash && !hasarg && maptype != 1)
622b66bab38SBram Moolenaar 	{
623b66bab38SBram Moolenaar 	    // need to loop over all global hash lists
624b66bab38SBram Moolenaar 	    for (hash = 0; hash < 256 && !got_int; ++hash)
625b66bab38SBram Moolenaar 	    {
626b66bab38SBram Moolenaar 		if (abbrev)
627b66bab38SBram Moolenaar 		{
628b66bab38SBram Moolenaar 		    if (hash != 0)	// there is only one abbreviation list
629b66bab38SBram Moolenaar 			break;
630b66bab38SBram Moolenaar 		    mp = curbuf->b_first_abbr;
631b66bab38SBram Moolenaar 		}
632b66bab38SBram Moolenaar 		else
633b66bab38SBram Moolenaar 		    mp = curbuf->b_maphash[hash];
634b66bab38SBram Moolenaar 		for ( ; mp != NULL && !got_int; mp = mp->m_next)
635b66bab38SBram Moolenaar 		{
636b66bab38SBram Moolenaar 		    // check entries with the same mode
637fafb4b18SBram Moolenaar 		    if (!mp->m_simplified && (mp->m_mode & mode) != 0)
638b66bab38SBram Moolenaar 		    {
639b66bab38SBram Moolenaar 			if (!haskey)		    // show all entries
640b66bab38SBram Moolenaar 			{
641b66bab38SBram Moolenaar 			    showmap(mp, TRUE);
642b66bab38SBram Moolenaar 			    did_local = TRUE;
643b66bab38SBram Moolenaar 			}
644b66bab38SBram Moolenaar 			else
645b66bab38SBram Moolenaar 			{
646b66bab38SBram Moolenaar 			    n = mp->m_keylen;
647b66bab38SBram Moolenaar 			    if (STRNCMP(mp->m_keys, keys,
648b66bab38SBram Moolenaar 					     (size_t)(n < len ? n : len)) == 0)
649b66bab38SBram Moolenaar 			    {
650b66bab38SBram Moolenaar 				showmap(mp, TRUE);
651b66bab38SBram Moolenaar 				did_local = TRUE;
652b66bab38SBram Moolenaar 			    }
653b66bab38SBram Moolenaar 			}
654b66bab38SBram Moolenaar 		    }
655b66bab38SBram Moolenaar 		}
656b66bab38SBram Moolenaar 	    }
657b66bab38SBram Moolenaar 	}
658b66bab38SBram Moolenaar 
659b66bab38SBram Moolenaar 	// Find an entry in the maphash[] list that matches.
660459fd785SBram Moolenaar 	// For :unmap we may loop two times: once to try to unmap an entry with
661459fd785SBram Moolenaar 	// a matching 'from' part, a second time, if the first fails, to unmap
662459fd785SBram Moolenaar 	// an entry with a matching 'to' part. This was done to allow ":ab foo
663459fd785SBram Moolenaar 	// bar" to be unmapped by typing ":unab foo", where "foo" will be
664459fd785SBram Moolenaar 	// replaced by "bar" because of the abbreviation.
665b66bab38SBram Moolenaar 	for (round = 0; (round == 0 || maptype == 1) && round <= 1
666b66bab38SBram Moolenaar 					       && !did_it && !got_int; ++round)
667b66bab38SBram Moolenaar 	{
668b66bab38SBram Moolenaar 	    // need to loop over all hash lists
669b66bab38SBram Moolenaar 	    for (hash = 0; hash < 256 && !got_int; ++hash)
670b66bab38SBram Moolenaar 	    {
671b66bab38SBram Moolenaar 		if (abbrev)
672b66bab38SBram Moolenaar 		{
673b66bab38SBram Moolenaar 		    if (hash > 0)	// there is only one abbreviation list
674b66bab38SBram Moolenaar 			break;
675b66bab38SBram Moolenaar 		    mpp = abbr_table;
676b66bab38SBram Moolenaar 		}
677b66bab38SBram Moolenaar 		else
678b66bab38SBram Moolenaar 		    mpp = &(map_table[hash]);
679b66bab38SBram Moolenaar 		for (mp = *mpp; mp != NULL && !got_int; mp = *mpp)
680b66bab38SBram Moolenaar 		{
681b66bab38SBram Moolenaar 
682fafb4b18SBram Moolenaar 		    if ((mp->m_mode & mode) == 0)
683b66bab38SBram Moolenaar 		    {
684fafb4b18SBram Moolenaar 			// skip entries with wrong mode
685b66bab38SBram Moolenaar 			mpp = &(mp->m_next);
686b66bab38SBram Moolenaar 			continue;
687b66bab38SBram Moolenaar 		    }
688b66bab38SBram Moolenaar 		    if (!haskey)	// show all entries
689b66bab38SBram Moolenaar 		    {
690fafb4b18SBram Moolenaar 			if (!mp->m_simplified)
691fafb4b18SBram Moolenaar 			{
692b66bab38SBram Moolenaar 			    showmap(mp, map_table != maphash);
693b66bab38SBram Moolenaar 			    did_it = TRUE;
694b66bab38SBram Moolenaar 			}
695fafb4b18SBram Moolenaar 		    }
696b66bab38SBram Moolenaar 		    else	// do we have a match?
697b66bab38SBram Moolenaar 		    {
698b66bab38SBram Moolenaar 			if (round)	// second round: Try unmap "rhs" string
699b66bab38SBram Moolenaar 			{
700b66bab38SBram Moolenaar 			    n = (int)STRLEN(mp->m_str);
701b66bab38SBram Moolenaar 			    p = mp->m_str;
702b66bab38SBram Moolenaar 			}
703b66bab38SBram Moolenaar 			else
704b66bab38SBram Moolenaar 			{
705b66bab38SBram Moolenaar 			    n = mp->m_keylen;
706b66bab38SBram Moolenaar 			    p = mp->m_keys;
707b66bab38SBram Moolenaar 			}
708b66bab38SBram Moolenaar 			if (STRNCMP(p, keys, (size_t)(n < len ? n : len)) == 0)
709b66bab38SBram Moolenaar 			{
710459fd785SBram Moolenaar 			    if (maptype == 1)
711b66bab38SBram Moolenaar 			    {
712459fd785SBram Moolenaar 				// Delete entry.
713459fd785SBram Moolenaar 				// Only accept a full match.  For abbreviations
714459fd785SBram Moolenaar 				// we ignore trailing space when matching with
715459fd785SBram Moolenaar 				// the "lhs", since an abbreviation can't have
716b66bab38SBram Moolenaar 				// trailing space.
717b66bab38SBram Moolenaar 				if (n != len && (!abbrev || round || n > len
718b66bab38SBram Moolenaar 					       || *skipwhite(keys + n) != NUL))
719b66bab38SBram Moolenaar 				{
720b66bab38SBram Moolenaar 				    mpp = &(mp->m_next);
721b66bab38SBram Moolenaar 				    continue;
722b66bab38SBram Moolenaar 				}
723459fd785SBram Moolenaar 				// We reset the indicated mode bits. If nothing
724459fd785SBram Moolenaar 				// is left the entry is deleted below.
725b66bab38SBram Moolenaar 				mp->m_mode &= ~mode;
726b66bab38SBram Moolenaar 				did_it = TRUE;	// remember we did something
727b66bab38SBram Moolenaar 			    }
728b66bab38SBram Moolenaar 			    else if (!hasarg)	// show matching entry
729b66bab38SBram Moolenaar 			    {
730fafb4b18SBram Moolenaar 				if (!mp->m_simplified)
731fafb4b18SBram Moolenaar 				{
732b66bab38SBram Moolenaar 				    showmap(mp, map_table != maphash);
733b66bab38SBram Moolenaar 				    did_it = TRUE;
734b66bab38SBram Moolenaar 				}
735fafb4b18SBram Moolenaar 			    }
736b66bab38SBram Moolenaar 			    else if (n != len)	// new entry is ambiguous
737b66bab38SBram Moolenaar 			    {
738b66bab38SBram Moolenaar 				mpp = &(mp->m_next);
739b66bab38SBram Moolenaar 				continue;
740b66bab38SBram Moolenaar 			    }
741b66bab38SBram Moolenaar 			    else if (unique)
742b66bab38SBram Moolenaar 			    {
743b66bab38SBram Moolenaar 				if (abbrev)
744459fd785SBram Moolenaar 				    semsg(_(
745459fd785SBram Moolenaar 				   "E226: abbreviation already exists for %s"),
746b66bab38SBram Moolenaar 					    p);
747b66bab38SBram Moolenaar 				else
748459fd785SBram Moolenaar 				    semsg(_(
749459fd785SBram Moolenaar 					"E227: mapping already exists for %s"),
750459fd785SBram Moolenaar 					    p);
751b66bab38SBram Moolenaar 				retval = 5;
752b66bab38SBram Moolenaar 				goto theend;
753b66bab38SBram Moolenaar 			    }
754459fd785SBram Moolenaar 			    else
755b66bab38SBram Moolenaar 			    {
756459fd785SBram Moolenaar 				// new rhs for existing entry
757b66bab38SBram Moolenaar 				mp->m_mode &= ~mode;	// remove mode bits
758b66bab38SBram Moolenaar 				if (mp->m_mode == 0 && !did_it) // reuse entry
759b66bab38SBram Moolenaar 				{
760459fd785SBram Moolenaar 				    char_u *newstr = vim_strsave(rhs);
761459fd785SBram Moolenaar 
762b66bab38SBram Moolenaar 				    if (newstr == NULL)
763b66bab38SBram Moolenaar 				    {
764b66bab38SBram Moolenaar 					retval = 4;		// no mem
765b66bab38SBram Moolenaar 					goto theend;
766b66bab38SBram Moolenaar 				    }
767b66bab38SBram Moolenaar 				    vim_free(mp->m_str);
768b66bab38SBram Moolenaar 				    mp->m_str = newstr;
769b66bab38SBram Moolenaar 				    vim_free(mp->m_orig_str);
770b66bab38SBram Moolenaar 				    mp->m_orig_str = vim_strsave(orig_rhs);
771b66bab38SBram Moolenaar 				    mp->m_noremap = noremap;
772b66bab38SBram Moolenaar 				    mp->m_nowait = nowait;
773b66bab38SBram Moolenaar 				    mp->m_silent = silent;
774b66bab38SBram Moolenaar 				    mp->m_mode = mode;
775459fd785SBram Moolenaar 				    mp->m_simplified =
776459fd785SBram Moolenaar 						 did_simplify && keyround == 1;
777b66bab38SBram Moolenaar #ifdef FEAT_EVAL
778b66bab38SBram Moolenaar 				    mp->m_expr = expr;
779b66bab38SBram Moolenaar 				    mp->m_script_ctx = current_sctx;
7801a47ae32SBram Moolenaar 				    mp->m_script_ctx.sc_lnum += SOURCING_LNUM;
781b66bab38SBram Moolenaar #endif
782b66bab38SBram Moolenaar 				    did_it = TRUE;
783b66bab38SBram Moolenaar 				}
784b66bab38SBram Moolenaar 			    }
785b66bab38SBram Moolenaar 			    if (mp->m_mode == 0)  // entry can be deleted
786b66bab38SBram Moolenaar 			    {
787b66bab38SBram Moolenaar 				map_free(mpp);
788b66bab38SBram Moolenaar 				continue;	// continue with *mpp
789b66bab38SBram Moolenaar 			    }
790b66bab38SBram Moolenaar 
791459fd785SBram Moolenaar 			    // May need to put this entry into another hash
792459fd785SBram Moolenaar 			    // list.
793b66bab38SBram Moolenaar 			    new_hash = MAP_HASH(mp->m_mode, mp->m_keys[0]);
794b66bab38SBram Moolenaar 			    if (!abbrev && new_hash != hash)
795b66bab38SBram Moolenaar 			    {
796b66bab38SBram Moolenaar 				*mpp = mp->m_next;
797b66bab38SBram Moolenaar 				mp->m_next = map_table[new_hash];
798b66bab38SBram Moolenaar 				map_table[new_hash] = mp;
799b66bab38SBram Moolenaar 
800b66bab38SBram Moolenaar 				continue;	// continue with *mpp
801b66bab38SBram Moolenaar 			    }
802b66bab38SBram Moolenaar 			}
803b66bab38SBram Moolenaar 		    }
804b66bab38SBram Moolenaar 		    mpp = &(mp->m_next);
805b66bab38SBram Moolenaar 		}
806b66bab38SBram Moolenaar 	    }
807b66bab38SBram Moolenaar 	}
808b66bab38SBram Moolenaar 
809459fd785SBram Moolenaar 	if (maptype == 1)
810b66bab38SBram Moolenaar 	{
811459fd785SBram Moolenaar 	    // delete entry
812b66bab38SBram Moolenaar 	    if (!did_it)
813b66bab38SBram Moolenaar 		retval = 2;	// no match
814b66bab38SBram Moolenaar 	    else if (*keys == Ctrl_C)
815b66bab38SBram Moolenaar 	    {
816b66bab38SBram Moolenaar 		// If CTRL-C has been unmapped, reuse it for Interrupting.
817b66bab38SBram Moolenaar 		if (map_table == curbuf->b_maphash)
818b66bab38SBram Moolenaar 		    curbuf->b_mapped_ctrl_c &= ~mode;
819b66bab38SBram Moolenaar 		else
820b66bab38SBram Moolenaar 		    mapped_ctrl_c &= ~mode;
821b66bab38SBram Moolenaar 	    }
822459fd785SBram Moolenaar 	    continue;
823b66bab38SBram Moolenaar 	}
824b66bab38SBram Moolenaar 
825459fd785SBram Moolenaar 	if (!haskey || !hasarg)
826b66bab38SBram Moolenaar 	{
827459fd785SBram Moolenaar 	    // print entries
828b66bab38SBram Moolenaar 	    if (!did_it && !did_local)
829b66bab38SBram Moolenaar 	    {
830b66bab38SBram Moolenaar 		if (abbrev)
831b66bab38SBram Moolenaar 		    msg(_("No abbreviation found"));
832b66bab38SBram Moolenaar 		else
833b66bab38SBram Moolenaar 		    msg(_("No mapping found"));
834b66bab38SBram Moolenaar 	    }
835b66bab38SBram Moolenaar 	    goto theend;    // listing finished
836b66bab38SBram Moolenaar 	}
837b66bab38SBram Moolenaar 
838459fd785SBram Moolenaar 	if (did_it)
839459fd785SBram Moolenaar 	    continue;	// have added the new entry already
840b66bab38SBram Moolenaar 
841b66bab38SBram Moolenaar 	// Get here when adding a new entry to the maphash[] list or abbrlist.
8425a80f8adSBram Moolenaar 	if (map_add(map_table, abbr_table, keys, rhs, orig_rhs,
8435a80f8adSBram Moolenaar 		    noremap, nowait, silent, mode, abbrev,
844b66bab38SBram Moolenaar #ifdef FEAT_EVAL
8455a80f8adSBram Moolenaar 		    expr, /* sid */ -1, /* lnum */ 0,
846b66bab38SBram Moolenaar #endif
8474c9243f9SBram Moolenaar 		    did_simplify && keyround == 1) == FAIL)
848b66bab38SBram Moolenaar 	{
8494c9243f9SBram Moolenaar 	    retval = 4;	    // no mem
8504c9243f9SBram Moolenaar 	    goto theend;
851b66bab38SBram Moolenaar 	}
852459fd785SBram Moolenaar     }
853b66bab38SBram Moolenaar 
854b66bab38SBram Moolenaar theend:
855b66bab38SBram Moolenaar     vim_free(keys_buf);
856459fd785SBram Moolenaar     vim_free(alt_keys_buf);
857b66bab38SBram Moolenaar     vim_free(arg_buf);
858b66bab38SBram Moolenaar     return retval;
859b66bab38SBram Moolenaar }
860b66bab38SBram Moolenaar 
861b66bab38SBram Moolenaar /*
862b66bab38SBram Moolenaar  * Get the mapping mode from the command name.
863b66bab38SBram Moolenaar  */
864b66bab38SBram Moolenaar     static int
get_map_mode(char_u ** cmdp,int forceit)865b66bab38SBram Moolenaar get_map_mode(char_u **cmdp, int forceit)
866b66bab38SBram Moolenaar {
867b66bab38SBram Moolenaar     char_u	*p;
868b66bab38SBram Moolenaar     int		modec;
869b66bab38SBram Moolenaar     int		mode;
870b66bab38SBram Moolenaar 
871b66bab38SBram Moolenaar     p = *cmdp;
872b66bab38SBram Moolenaar     modec = *p++;
873b66bab38SBram Moolenaar     if (modec == 'i')
874b66bab38SBram Moolenaar 	mode = INSERT;				// :imap
875b66bab38SBram Moolenaar     else if (modec == 'l')
876b66bab38SBram Moolenaar 	mode = LANGMAP;				// :lmap
877b66bab38SBram Moolenaar     else if (modec == 'c')
878b66bab38SBram Moolenaar 	mode = CMDLINE;				// :cmap
879b66bab38SBram Moolenaar     else if (modec == 'n' && *p != 'o')		    // avoid :noremap
880b66bab38SBram Moolenaar 	mode = NORMAL;				// :nmap
881b66bab38SBram Moolenaar     else if (modec == 'v')
882b66bab38SBram Moolenaar 	mode = VISUAL + SELECTMODE;		// :vmap
883b66bab38SBram Moolenaar     else if (modec == 'x')
884b66bab38SBram Moolenaar 	mode = VISUAL;				// :xmap
885b66bab38SBram Moolenaar     else if (modec == 's')
886b66bab38SBram Moolenaar 	mode = SELECTMODE;			// :smap
887b66bab38SBram Moolenaar     else if (modec == 'o')
888b66bab38SBram Moolenaar 	mode = OP_PENDING;			// :omap
889b66bab38SBram Moolenaar     else if (modec == 't')
890b66bab38SBram Moolenaar 	mode = TERMINAL;			// :tmap
891b66bab38SBram Moolenaar     else
892b66bab38SBram Moolenaar     {
893b66bab38SBram Moolenaar 	--p;
894b66bab38SBram Moolenaar 	if (forceit)
895b66bab38SBram Moolenaar 	    mode = INSERT + CMDLINE;		// :map !
896b66bab38SBram Moolenaar 	else
897b66bab38SBram Moolenaar 	    mode = VISUAL + SELECTMODE + NORMAL + OP_PENDING;// :map
898b66bab38SBram Moolenaar     }
899b66bab38SBram Moolenaar 
900b66bab38SBram Moolenaar     *cmdp = p;
901b66bab38SBram Moolenaar     return mode;
902b66bab38SBram Moolenaar }
903b66bab38SBram Moolenaar 
904b66bab38SBram Moolenaar /*
905b66bab38SBram Moolenaar  * Clear all mappings or abbreviations.
906b66bab38SBram Moolenaar  * 'abbr' should be FALSE for mappings, TRUE for abbreviations.
907b66bab38SBram Moolenaar  */
908b66bab38SBram Moolenaar     static void
map_clear(char_u * cmdp,char_u * arg UNUSED,int forceit,int abbr)909b66bab38SBram Moolenaar map_clear(
910b66bab38SBram Moolenaar     char_u	*cmdp,
911b66bab38SBram Moolenaar     char_u	*arg UNUSED,
912b66bab38SBram Moolenaar     int		forceit,
913b66bab38SBram Moolenaar     int		abbr)
914b66bab38SBram Moolenaar {
915b66bab38SBram Moolenaar     int		mode;
916b66bab38SBram Moolenaar     int		local;
917b66bab38SBram Moolenaar 
918b66bab38SBram Moolenaar     local = (STRCMP(arg, "<buffer>") == 0);
919b66bab38SBram Moolenaar     if (!local && *arg != NUL)
920b66bab38SBram Moolenaar     {
921b66bab38SBram Moolenaar 	emsg(_(e_invarg));
922b66bab38SBram Moolenaar 	return;
923b66bab38SBram Moolenaar     }
924b66bab38SBram Moolenaar 
925b66bab38SBram Moolenaar     mode = get_map_mode(&cmdp, forceit);
926b66bab38SBram Moolenaar     map_clear_int(curbuf, mode, local, abbr);
927b66bab38SBram Moolenaar }
928b66bab38SBram Moolenaar 
929b66bab38SBram Moolenaar /*
930b66bab38SBram Moolenaar  * Clear all mappings in "mode".
931b66bab38SBram Moolenaar  */
932b66bab38SBram Moolenaar     void
map_clear_int(buf_T * buf,int mode,int local,int abbr)933b66bab38SBram Moolenaar map_clear_int(
934b66bab38SBram Moolenaar     buf_T	*buf,		// buffer for local mappings
935b66bab38SBram Moolenaar     int		mode,		// mode in which to delete
936b66bab38SBram Moolenaar     int		local,		// TRUE for buffer-local mappings
937b66bab38SBram Moolenaar     int		abbr)		// TRUE for abbreviations
938b66bab38SBram Moolenaar {
939b66bab38SBram Moolenaar     mapblock_T	*mp, **mpp;
940b66bab38SBram Moolenaar     int		hash;
941b66bab38SBram Moolenaar     int		new_hash;
942b66bab38SBram Moolenaar 
943b66bab38SBram Moolenaar     validate_maphash();
944b66bab38SBram Moolenaar 
945b66bab38SBram Moolenaar     for (hash = 0; hash < 256; ++hash)
946b66bab38SBram Moolenaar     {
947b66bab38SBram Moolenaar 	if (abbr)
948b66bab38SBram Moolenaar 	{
949b66bab38SBram Moolenaar 	    if (hash > 0)	// there is only one abbrlist
950b66bab38SBram Moolenaar 		break;
951b66bab38SBram Moolenaar 	    if (local)
952b66bab38SBram Moolenaar 		mpp = &buf->b_first_abbr;
953b66bab38SBram Moolenaar 	    else
954b66bab38SBram Moolenaar 		mpp = &first_abbr;
955b66bab38SBram Moolenaar 	}
956b66bab38SBram Moolenaar 	else
957b66bab38SBram Moolenaar 	{
958b66bab38SBram Moolenaar 	    if (local)
959b66bab38SBram Moolenaar 		mpp = &buf->b_maphash[hash];
960b66bab38SBram Moolenaar 	    else
961b66bab38SBram Moolenaar 		mpp = &maphash[hash];
962b66bab38SBram Moolenaar 	}
963b66bab38SBram Moolenaar 	while (*mpp != NULL)
964b66bab38SBram Moolenaar 	{
965b66bab38SBram Moolenaar 	    mp = *mpp;
966b66bab38SBram Moolenaar 	    if (mp->m_mode & mode)
967b66bab38SBram Moolenaar 	    {
968b66bab38SBram Moolenaar 		mp->m_mode &= ~mode;
969b66bab38SBram Moolenaar 		if (mp->m_mode == 0) // entry can be deleted
970b66bab38SBram Moolenaar 		{
971b66bab38SBram Moolenaar 		    map_free(mpp);
972b66bab38SBram Moolenaar 		    continue;
973b66bab38SBram Moolenaar 		}
974b66bab38SBram Moolenaar 		// May need to put this entry into another hash list.
975b66bab38SBram Moolenaar 		new_hash = MAP_HASH(mp->m_mode, mp->m_keys[0]);
976b66bab38SBram Moolenaar 		if (!abbr && new_hash != hash)
977b66bab38SBram Moolenaar 		{
978b66bab38SBram Moolenaar 		    *mpp = mp->m_next;
979b66bab38SBram Moolenaar 		    if (local)
980b66bab38SBram Moolenaar 		    {
981b66bab38SBram Moolenaar 			mp->m_next = buf->b_maphash[new_hash];
982b66bab38SBram Moolenaar 			buf->b_maphash[new_hash] = mp;
983b66bab38SBram Moolenaar 		    }
984b66bab38SBram Moolenaar 		    else
985b66bab38SBram Moolenaar 		    {
986b66bab38SBram Moolenaar 			mp->m_next = maphash[new_hash];
987b66bab38SBram Moolenaar 			maphash[new_hash] = mp;
988b66bab38SBram Moolenaar 		    }
989b66bab38SBram Moolenaar 		    continue;		// continue with *mpp
990b66bab38SBram Moolenaar 		}
991b66bab38SBram Moolenaar 	    }
992b66bab38SBram Moolenaar 	    mpp = &(mp->m_next);
993b66bab38SBram Moolenaar 	}
994b66bab38SBram Moolenaar     }
995b66bab38SBram Moolenaar }
996b66bab38SBram Moolenaar 
997b66bab38SBram Moolenaar #if defined(FEAT_EVAL) || defined(PROTO)
998b66bab38SBram Moolenaar     int
mode_str2flags(char_u * modechars)999581ba39aSBram Moolenaar mode_str2flags(char_u *modechars)
1000b66bab38SBram Moolenaar {
1001b66bab38SBram Moolenaar     int		mode = 0;
1002b66bab38SBram Moolenaar 
1003b66bab38SBram Moolenaar     if (vim_strchr(modechars, 'n') != NULL)
1004b66bab38SBram Moolenaar 	mode |= NORMAL;
1005b66bab38SBram Moolenaar     if (vim_strchr(modechars, 'v') != NULL)
1006b66bab38SBram Moolenaar 	mode |= VISUAL + SELECTMODE;
1007b66bab38SBram Moolenaar     if (vim_strchr(modechars, 'x') != NULL)
1008b66bab38SBram Moolenaar 	mode |= VISUAL;
1009b66bab38SBram Moolenaar     if (vim_strchr(modechars, 's') != NULL)
1010b66bab38SBram Moolenaar 	mode |= SELECTMODE;
1011b66bab38SBram Moolenaar     if (vim_strchr(modechars, 'o') != NULL)
1012b66bab38SBram Moolenaar 	mode |= OP_PENDING;
1013b66bab38SBram Moolenaar     if (vim_strchr(modechars, 'i') != NULL)
1014b66bab38SBram Moolenaar 	mode |= INSERT;
1015b66bab38SBram Moolenaar     if (vim_strchr(modechars, 'l') != NULL)
1016b66bab38SBram Moolenaar 	mode |= LANGMAP;
1017b66bab38SBram Moolenaar     if (vim_strchr(modechars, 'c') != NULL)
1018b66bab38SBram Moolenaar 	mode |= CMDLINE;
1019b66bab38SBram Moolenaar 
1020581ba39aSBram Moolenaar     return mode;
1021581ba39aSBram Moolenaar }
1022581ba39aSBram Moolenaar 
1023581ba39aSBram Moolenaar /*
1024581ba39aSBram Moolenaar  * Return TRUE if a map exists that has "str" in the rhs for mode "modechars".
1025581ba39aSBram Moolenaar  * Recognize termcap codes in "str".
1026581ba39aSBram Moolenaar  * Also checks mappings local to the current buffer.
1027581ba39aSBram Moolenaar  */
1028581ba39aSBram Moolenaar     int
map_to_exists(char_u * str,char_u * modechars,int abbr)1029581ba39aSBram Moolenaar map_to_exists(char_u *str, char_u *modechars, int abbr)
1030581ba39aSBram Moolenaar {
1031581ba39aSBram Moolenaar     char_u	*rhs;
1032581ba39aSBram Moolenaar     char_u	*buf;
1033581ba39aSBram Moolenaar     int		retval;
1034581ba39aSBram Moolenaar 
1035459fd785SBram Moolenaar     rhs = replace_termcodes(str, &buf, REPTERM_DO_LT, NULL);
1036581ba39aSBram Moolenaar 
1037581ba39aSBram Moolenaar     retval = map_to_exists_mode(rhs, mode_str2flags(modechars), abbr);
1038b66bab38SBram Moolenaar     vim_free(buf);
1039b66bab38SBram Moolenaar 
1040b66bab38SBram Moolenaar     return retval;
1041b66bab38SBram Moolenaar }
1042b66bab38SBram Moolenaar #endif
1043b66bab38SBram Moolenaar 
1044b66bab38SBram Moolenaar /*
1045b66bab38SBram Moolenaar  * Return TRUE if a map exists that has "str" in the rhs for mode "mode".
1046b66bab38SBram Moolenaar  * Also checks mappings local to the current buffer.
1047b66bab38SBram Moolenaar  */
1048b66bab38SBram Moolenaar     int
map_to_exists_mode(char_u * rhs,int mode,int abbr)1049b66bab38SBram Moolenaar map_to_exists_mode(char_u *rhs, int mode, int abbr)
1050b66bab38SBram Moolenaar {
1051b66bab38SBram Moolenaar     mapblock_T	*mp;
1052b66bab38SBram Moolenaar     int		hash;
1053b66bab38SBram Moolenaar     int		exp_buffer = FALSE;
1054b66bab38SBram Moolenaar 
1055b66bab38SBram Moolenaar     validate_maphash();
1056b66bab38SBram Moolenaar 
1057b66bab38SBram Moolenaar     // Do it twice: once for global maps and once for local maps.
1058b66bab38SBram Moolenaar     for (;;)
1059b66bab38SBram Moolenaar     {
1060b66bab38SBram Moolenaar 	for (hash = 0; hash < 256; ++hash)
1061b66bab38SBram Moolenaar 	{
1062b66bab38SBram Moolenaar 	    if (abbr)
1063b66bab38SBram Moolenaar 	    {
1064b66bab38SBram Moolenaar 		if (hash > 0)		// there is only one abbr list
1065b66bab38SBram Moolenaar 		    break;
1066b66bab38SBram Moolenaar 		if (exp_buffer)
1067b66bab38SBram Moolenaar 		    mp = curbuf->b_first_abbr;
1068b66bab38SBram Moolenaar 		else
1069b66bab38SBram Moolenaar 		    mp = first_abbr;
1070b66bab38SBram Moolenaar 	    }
1071b66bab38SBram Moolenaar 	    else if (exp_buffer)
1072b66bab38SBram Moolenaar 		mp = curbuf->b_maphash[hash];
1073b66bab38SBram Moolenaar 	    else
1074b66bab38SBram Moolenaar 		mp = maphash[hash];
1075b66bab38SBram Moolenaar 	    for (; mp; mp = mp->m_next)
1076b66bab38SBram Moolenaar 	    {
1077b66bab38SBram Moolenaar 		if ((mp->m_mode & mode)
1078b66bab38SBram Moolenaar 			&& strstr((char *)mp->m_str, (char *)rhs) != NULL)
1079b66bab38SBram Moolenaar 		    return TRUE;
1080b66bab38SBram Moolenaar 	    }
1081b66bab38SBram Moolenaar 	}
1082b66bab38SBram Moolenaar 	if (exp_buffer)
1083b66bab38SBram Moolenaar 	    break;
1084b66bab38SBram Moolenaar 	exp_buffer = TRUE;
1085b66bab38SBram Moolenaar     }
1086b66bab38SBram Moolenaar 
1087b66bab38SBram Moolenaar     return FALSE;
1088b66bab38SBram Moolenaar }
1089b66bab38SBram Moolenaar 
1090b66bab38SBram Moolenaar /*
1091b66bab38SBram Moolenaar  * Used below when expanding mapping/abbreviation names.
1092b66bab38SBram Moolenaar  */
1093b66bab38SBram Moolenaar static int	expand_mapmodes = 0;
1094b66bab38SBram Moolenaar static int	expand_isabbrev = 0;
1095b66bab38SBram Moolenaar static int	expand_buffer = FALSE;
1096b66bab38SBram Moolenaar 
1097b66bab38SBram Moolenaar /*
10987f51bbe0SBram Moolenaar  * Translate an internal mapping/abbreviation representation into the
10997f51bbe0SBram Moolenaar  * corresponding external one recognized by :map/:abbrev commands.
11007f51bbe0SBram Moolenaar  * Respects the current B/k/< settings of 'cpoption'.
11017f51bbe0SBram Moolenaar  *
11027f51bbe0SBram Moolenaar  * This function is called when expanding mappings/abbreviations on the
11037f51bbe0SBram Moolenaar  * command-line.
11047f51bbe0SBram Moolenaar  *
11057f51bbe0SBram Moolenaar  * It uses a growarray to build the translation string since the latter can be
11067f51bbe0SBram Moolenaar  * wider than the original description. The caller has to free the string
11077f51bbe0SBram Moolenaar  * afterwards.
11087f51bbe0SBram Moolenaar  *
11097f51bbe0SBram Moolenaar  * Returns NULL when there is a problem.
11107f51bbe0SBram Moolenaar  */
11117f51bbe0SBram Moolenaar     static char_u *
translate_mapping(char_u * str)11127f51bbe0SBram Moolenaar translate_mapping(char_u *str)
11137f51bbe0SBram Moolenaar {
11147f51bbe0SBram Moolenaar     garray_T	ga;
11157f51bbe0SBram Moolenaar     int		c;
11167f51bbe0SBram Moolenaar     int		modifiers;
11177f51bbe0SBram Moolenaar     int		cpo_bslash;
11187f51bbe0SBram Moolenaar     int		cpo_special;
11197f51bbe0SBram Moolenaar 
11207f51bbe0SBram Moolenaar     ga_init(&ga);
11217f51bbe0SBram Moolenaar     ga.ga_itemsize = 1;
11227f51bbe0SBram Moolenaar     ga.ga_growsize = 40;
11237f51bbe0SBram Moolenaar 
11247f51bbe0SBram Moolenaar     cpo_bslash = (vim_strchr(p_cpo, CPO_BSLASH) != NULL);
11257f51bbe0SBram Moolenaar     cpo_special = (vim_strchr(p_cpo, CPO_SPECI) != NULL);
11267f51bbe0SBram Moolenaar 
11277f51bbe0SBram Moolenaar     for (; *str; ++str)
11287f51bbe0SBram Moolenaar     {
11297f51bbe0SBram Moolenaar 	c = *str;
11307f51bbe0SBram Moolenaar 	if (c == K_SPECIAL && str[1] != NUL && str[2] != NUL)
11317f51bbe0SBram Moolenaar 	{
11327f51bbe0SBram Moolenaar 	    modifiers = 0;
11337f51bbe0SBram Moolenaar 	    if (str[1] == KS_MODIFIER)
11347f51bbe0SBram Moolenaar 	    {
11357f51bbe0SBram Moolenaar 		str++;
11367f51bbe0SBram Moolenaar 		modifiers = *++str;
11377f51bbe0SBram Moolenaar 		c = *++str;
11387f51bbe0SBram Moolenaar 	    }
11397f51bbe0SBram Moolenaar 	    if (c == K_SPECIAL && str[1] != NUL && str[2] != NUL)
11407f51bbe0SBram Moolenaar 	    {
11417f51bbe0SBram Moolenaar 		if (cpo_special)
11427f51bbe0SBram Moolenaar 		{
11437f51bbe0SBram Moolenaar 		    ga_clear(&ga);
11447f51bbe0SBram Moolenaar 		    return NULL;
11457f51bbe0SBram Moolenaar 		}
11467f51bbe0SBram Moolenaar 		c = TO_SPECIAL(str[1], str[2]);
11477f51bbe0SBram Moolenaar 		if (c == K_ZERO)	// display <Nul> as ^@
11487f51bbe0SBram Moolenaar 		    c = NUL;
11497f51bbe0SBram Moolenaar 		str += 2;
11507f51bbe0SBram Moolenaar 	    }
11517f51bbe0SBram Moolenaar 	    if (IS_SPECIAL(c) || modifiers)	// special key
11527f51bbe0SBram Moolenaar 	    {
11537f51bbe0SBram Moolenaar 		if (cpo_special)
11547f51bbe0SBram Moolenaar 		{
11557f51bbe0SBram Moolenaar 		    ga_clear(&ga);
11567f51bbe0SBram Moolenaar 		    return NULL;
11577f51bbe0SBram Moolenaar 		}
11587f51bbe0SBram Moolenaar 		ga_concat(&ga, get_special_key_name(c, modifiers));
11597f51bbe0SBram Moolenaar 		continue; // for (str)
11607f51bbe0SBram Moolenaar 	    }
11617f51bbe0SBram Moolenaar 	}
11627f51bbe0SBram Moolenaar 	if (c == ' ' || c == '\t' || c == Ctrl_J || c == Ctrl_V
11637f51bbe0SBram Moolenaar 	    || (c == '<' && !cpo_special) || (c == '\\' && !cpo_bslash))
11647f51bbe0SBram Moolenaar 	    ga_append(&ga, cpo_bslash ? Ctrl_V : '\\');
11657f51bbe0SBram Moolenaar 	if (c)
11667f51bbe0SBram Moolenaar 	    ga_append(&ga, c);
11677f51bbe0SBram Moolenaar     }
11687f51bbe0SBram Moolenaar     ga_append(&ga, NUL);
11697f51bbe0SBram Moolenaar     return (char_u *)(ga.ga_data);
11707f51bbe0SBram Moolenaar }
11717f51bbe0SBram Moolenaar 
11727f51bbe0SBram Moolenaar /*
1173b66bab38SBram Moolenaar  * Work out what to complete when doing command line completion of mapping
1174b66bab38SBram Moolenaar  * or abbreviation names.
1175b66bab38SBram Moolenaar  */
1176b66bab38SBram Moolenaar     char_u *
set_context_in_map_cmd(expand_T * xp,char_u * cmd,char_u * arg,int forceit,int isabbrev,int isunmap,cmdidx_T cmdidx)1177b66bab38SBram Moolenaar set_context_in_map_cmd(
1178b66bab38SBram Moolenaar     expand_T	*xp,
1179b66bab38SBram Moolenaar     char_u	*cmd,
1180b66bab38SBram Moolenaar     char_u	*arg,
1181b66bab38SBram Moolenaar     int		forceit,	// TRUE if '!' given
1182b66bab38SBram Moolenaar     int		isabbrev,	// TRUE if abbreviation
1183b66bab38SBram Moolenaar     int		isunmap,	// TRUE if unmap/unabbrev command
1184b66bab38SBram Moolenaar     cmdidx_T	cmdidx)
1185b66bab38SBram Moolenaar {
1186b66bab38SBram Moolenaar     if (forceit && cmdidx != CMD_map && cmdidx != CMD_unmap)
1187b66bab38SBram Moolenaar 	xp->xp_context = EXPAND_NOTHING;
1188b66bab38SBram Moolenaar     else
1189b66bab38SBram Moolenaar     {
1190b66bab38SBram Moolenaar 	if (isunmap)
1191b66bab38SBram Moolenaar 	    expand_mapmodes = get_map_mode(&cmd, forceit || isabbrev);
1192b66bab38SBram Moolenaar 	else
1193b66bab38SBram Moolenaar 	{
1194b66bab38SBram Moolenaar 	    expand_mapmodes = INSERT + CMDLINE;
1195b66bab38SBram Moolenaar 	    if (!isabbrev)
1196b66bab38SBram Moolenaar 		expand_mapmodes += VISUAL + SELECTMODE + NORMAL + OP_PENDING;
1197b66bab38SBram Moolenaar 	}
1198b66bab38SBram Moolenaar 	expand_isabbrev = isabbrev;
1199b66bab38SBram Moolenaar 	xp->xp_context = EXPAND_MAPPINGS;
1200b66bab38SBram Moolenaar 	expand_buffer = FALSE;
1201b66bab38SBram Moolenaar 	for (;;)
1202b66bab38SBram Moolenaar 	{
1203b66bab38SBram Moolenaar 	    if (STRNCMP(arg, "<buffer>", 8) == 0)
1204b66bab38SBram Moolenaar 	    {
1205b66bab38SBram Moolenaar 		expand_buffer = TRUE;
1206b66bab38SBram Moolenaar 		arg = skipwhite(arg + 8);
1207b66bab38SBram Moolenaar 		continue;
1208b66bab38SBram Moolenaar 	    }
1209b66bab38SBram Moolenaar 	    if (STRNCMP(arg, "<unique>", 8) == 0)
1210b66bab38SBram Moolenaar 	    {
1211b66bab38SBram Moolenaar 		arg = skipwhite(arg + 8);
1212b66bab38SBram Moolenaar 		continue;
1213b66bab38SBram Moolenaar 	    }
1214b66bab38SBram Moolenaar 	    if (STRNCMP(arg, "<nowait>", 8) == 0)
1215b66bab38SBram Moolenaar 	    {
1216b66bab38SBram Moolenaar 		arg = skipwhite(arg + 8);
1217b66bab38SBram Moolenaar 		continue;
1218b66bab38SBram Moolenaar 	    }
1219b66bab38SBram Moolenaar 	    if (STRNCMP(arg, "<silent>", 8) == 0)
1220b66bab38SBram Moolenaar 	    {
1221b66bab38SBram Moolenaar 		arg = skipwhite(arg + 8);
1222b66bab38SBram Moolenaar 		continue;
1223b66bab38SBram Moolenaar 	    }
1224b66bab38SBram Moolenaar 	    if (STRNCMP(arg, "<special>", 9) == 0)
1225b66bab38SBram Moolenaar 	    {
1226b66bab38SBram Moolenaar 		arg = skipwhite(arg + 9);
1227b66bab38SBram Moolenaar 		continue;
1228b66bab38SBram Moolenaar 	    }
1229b66bab38SBram Moolenaar #ifdef FEAT_EVAL
1230b66bab38SBram Moolenaar 	    if (STRNCMP(arg, "<script>", 8) == 0)
1231b66bab38SBram Moolenaar 	    {
1232b66bab38SBram Moolenaar 		arg = skipwhite(arg + 8);
1233b66bab38SBram Moolenaar 		continue;
1234b66bab38SBram Moolenaar 	    }
1235b66bab38SBram Moolenaar 	    if (STRNCMP(arg, "<expr>", 6) == 0)
1236b66bab38SBram Moolenaar 	    {
1237b66bab38SBram Moolenaar 		arg = skipwhite(arg + 6);
1238b66bab38SBram Moolenaar 		continue;
1239b66bab38SBram Moolenaar 	    }
1240b66bab38SBram Moolenaar #endif
1241b66bab38SBram Moolenaar 	    break;
1242b66bab38SBram Moolenaar 	}
1243b66bab38SBram Moolenaar 	xp->xp_pattern = arg;
1244b66bab38SBram Moolenaar     }
1245b66bab38SBram Moolenaar 
1246b66bab38SBram Moolenaar     return NULL;
1247b66bab38SBram Moolenaar }
1248b66bab38SBram Moolenaar 
1249b66bab38SBram Moolenaar /*
1250b66bab38SBram Moolenaar  * Find all mapping/abbreviation names that match regexp "regmatch"'.
1251b66bab38SBram Moolenaar  * For command line expansion of ":[un]map" and ":[un]abbrev" in all modes.
1252b66bab38SBram Moolenaar  * Return OK if matches found, FAIL otherwise.
1253b66bab38SBram Moolenaar  */
1254b66bab38SBram Moolenaar     int
ExpandMappings(regmatch_T * regmatch,int * num_file,char_u *** file)1255b66bab38SBram Moolenaar ExpandMappings(
1256b66bab38SBram Moolenaar     regmatch_T	*regmatch,
1257b66bab38SBram Moolenaar     int		*num_file,
1258b66bab38SBram Moolenaar     char_u	***file)
1259b66bab38SBram Moolenaar {
1260b66bab38SBram Moolenaar     mapblock_T	*mp;
1261b66bab38SBram Moolenaar     int		hash;
1262b66bab38SBram Moolenaar     int		count;
1263b66bab38SBram Moolenaar     int		round;
1264b66bab38SBram Moolenaar     char_u	*p;
1265b66bab38SBram Moolenaar     int		i;
1266b66bab38SBram Moolenaar 
1267b66bab38SBram Moolenaar     validate_maphash();
1268b66bab38SBram Moolenaar 
1269b66bab38SBram Moolenaar     *num_file = 0;		    // return values in case of FAIL
1270b66bab38SBram Moolenaar     *file = NULL;
1271b66bab38SBram Moolenaar 
1272b66bab38SBram Moolenaar     // round == 1: Count the matches.
1273b66bab38SBram Moolenaar     // round == 2: Build the array to keep the matches.
1274b66bab38SBram Moolenaar     for (round = 1; round <= 2; ++round)
1275b66bab38SBram Moolenaar     {
1276b66bab38SBram Moolenaar 	count = 0;
1277b66bab38SBram Moolenaar 
1278b66bab38SBram Moolenaar 	for (i = 0; i < 7; ++i)
1279b66bab38SBram Moolenaar 	{
1280b66bab38SBram Moolenaar 	    if (i == 0)
1281b66bab38SBram Moolenaar 		p = (char_u *)"<silent>";
1282b66bab38SBram Moolenaar 	    else if (i == 1)
1283b66bab38SBram Moolenaar 		p = (char_u *)"<unique>";
1284b66bab38SBram Moolenaar #ifdef FEAT_EVAL
1285b66bab38SBram Moolenaar 	    else if (i == 2)
1286b66bab38SBram Moolenaar 		p = (char_u *)"<script>";
1287b66bab38SBram Moolenaar 	    else if (i == 3)
1288b66bab38SBram Moolenaar 		p = (char_u *)"<expr>";
1289b66bab38SBram Moolenaar #endif
1290b66bab38SBram Moolenaar 	    else if (i == 4 && !expand_buffer)
1291b66bab38SBram Moolenaar 		p = (char_u *)"<buffer>";
1292b66bab38SBram Moolenaar 	    else if (i == 5)
1293b66bab38SBram Moolenaar 		p = (char_u *)"<nowait>";
1294b66bab38SBram Moolenaar 	    else if (i == 6)
1295b66bab38SBram Moolenaar 		p = (char_u *)"<special>";
1296b66bab38SBram Moolenaar 	    else
1297b66bab38SBram Moolenaar 		continue;
1298b66bab38SBram Moolenaar 
1299b66bab38SBram Moolenaar 	    if (vim_regexec(regmatch, p, (colnr_T)0))
1300b66bab38SBram Moolenaar 	    {
1301b66bab38SBram Moolenaar 		if (round == 1)
1302b66bab38SBram Moolenaar 		    ++count;
1303b66bab38SBram Moolenaar 		else
1304b66bab38SBram Moolenaar 		    (*file)[count++] = vim_strsave(p);
1305b66bab38SBram Moolenaar 	    }
1306b66bab38SBram Moolenaar 	}
1307b66bab38SBram Moolenaar 
1308b66bab38SBram Moolenaar 	for (hash = 0; hash < 256; ++hash)
1309b66bab38SBram Moolenaar 	{
1310b66bab38SBram Moolenaar 	    if (expand_isabbrev)
1311b66bab38SBram Moolenaar 	    {
1312b66bab38SBram Moolenaar 		if (hash > 0)	// only one abbrev list
1313b66bab38SBram Moolenaar 		    break; // for (hash)
1314b66bab38SBram Moolenaar 		mp = first_abbr;
1315b66bab38SBram Moolenaar 	    }
1316b66bab38SBram Moolenaar 	    else if (expand_buffer)
1317b66bab38SBram Moolenaar 		mp = curbuf->b_maphash[hash];
1318b66bab38SBram Moolenaar 	    else
1319b66bab38SBram Moolenaar 		mp = maphash[hash];
1320b66bab38SBram Moolenaar 	    for (; mp; mp = mp->m_next)
1321b66bab38SBram Moolenaar 	    {
1322b66bab38SBram Moolenaar 		if (mp->m_mode & expand_mapmodes)
1323b66bab38SBram Moolenaar 		{
1324b66bab38SBram Moolenaar 		    p = translate_mapping(mp->m_keys);
1325b66bab38SBram Moolenaar 		    if (p != NULL && vim_regexec(regmatch, p, (colnr_T)0))
1326b66bab38SBram Moolenaar 		    {
1327b66bab38SBram Moolenaar 			if (round == 1)
1328b66bab38SBram Moolenaar 			    ++count;
1329b66bab38SBram Moolenaar 			else
1330b66bab38SBram Moolenaar 			{
1331b66bab38SBram Moolenaar 			    (*file)[count++] = p;
1332b66bab38SBram Moolenaar 			    p = NULL;
1333b66bab38SBram Moolenaar 			}
1334b66bab38SBram Moolenaar 		    }
1335b66bab38SBram Moolenaar 		    vim_free(p);
1336b66bab38SBram Moolenaar 		}
1337b66bab38SBram Moolenaar 	    } // for (mp)
1338b66bab38SBram Moolenaar 	} // for (hash)
1339b66bab38SBram Moolenaar 
1340b66bab38SBram Moolenaar 	if (count == 0)			// no match found
1341b66bab38SBram Moolenaar 	    break; // for (round)
1342b66bab38SBram Moolenaar 
1343b66bab38SBram Moolenaar 	if (round == 1)
1344b66bab38SBram Moolenaar 	{
1345b66bab38SBram Moolenaar 	    *file = ALLOC_MULT(char_u *, count);
1346b66bab38SBram Moolenaar 	    if (*file == NULL)
1347b66bab38SBram Moolenaar 		return FAIL;
1348b66bab38SBram Moolenaar 	}
1349b66bab38SBram Moolenaar     } // for (round)
1350b66bab38SBram Moolenaar 
1351b66bab38SBram Moolenaar     if (count > 1)
1352b66bab38SBram Moolenaar     {
1353b66bab38SBram Moolenaar 	char_u	**ptr1;
1354b66bab38SBram Moolenaar 	char_u	**ptr2;
1355b66bab38SBram Moolenaar 	char_u	**ptr3;
1356b66bab38SBram Moolenaar 
1357b66bab38SBram Moolenaar 	// Sort the matches
1358b66bab38SBram Moolenaar 	sort_strings(*file, count);
1359b66bab38SBram Moolenaar 
1360b66bab38SBram Moolenaar 	// Remove multiple entries
1361b66bab38SBram Moolenaar 	ptr1 = *file;
1362b66bab38SBram Moolenaar 	ptr2 = ptr1 + 1;
1363b66bab38SBram Moolenaar 	ptr3 = ptr1 + count;
1364b66bab38SBram Moolenaar 
1365b66bab38SBram Moolenaar 	while (ptr2 < ptr3)
1366b66bab38SBram Moolenaar 	{
1367b66bab38SBram Moolenaar 	    if (STRCMP(*ptr1, *ptr2))
1368b66bab38SBram Moolenaar 		*++ptr1 = *ptr2++;
1369b66bab38SBram Moolenaar 	    else
1370b66bab38SBram Moolenaar 	    {
1371b66bab38SBram Moolenaar 		vim_free(*ptr2++);
1372b66bab38SBram Moolenaar 		count--;
1373b66bab38SBram Moolenaar 	    }
1374b66bab38SBram Moolenaar 	}
1375b66bab38SBram Moolenaar     }
1376b66bab38SBram Moolenaar 
1377b66bab38SBram Moolenaar     *num_file = count;
1378b66bab38SBram Moolenaar     return (count == 0 ? FAIL : OK);
1379b66bab38SBram Moolenaar }
1380b66bab38SBram Moolenaar 
1381b66bab38SBram Moolenaar /*
1382b66bab38SBram Moolenaar  * Check for an abbreviation.
1383b66bab38SBram Moolenaar  * Cursor is at ptr[col].
1384b66bab38SBram Moolenaar  * When inserting, mincol is where insert started.
1385b66bab38SBram Moolenaar  * For the command line, mincol is what is to be skipped over.
1386b66bab38SBram Moolenaar  * "c" is the character typed before check_abbr was called.  It may have
1387b66bab38SBram Moolenaar  * ABBR_OFF added to avoid prepending a CTRL-V to it.
1388b66bab38SBram Moolenaar  *
1389b66bab38SBram Moolenaar  * Historic vi practice: The last character of an abbreviation must be an id
1390b66bab38SBram Moolenaar  * character ([a-zA-Z0-9_]). The characters in front of it must be all id
1391b66bab38SBram Moolenaar  * characters or all non-id characters. This allows for abbr. "#i" to
1392b66bab38SBram Moolenaar  * "#include".
1393b66bab38SBram Moolenaar  *
1394b66bab38SBram Moolenaar  * Vim addition: Allow for abbreviations that end in a non-keyword character.
1395b66bab38SBram Moolenaar  * Then there must be white space before the abbr.
1396b66bab38SBram Moolenaar  *
1397b66bab38SBram Moolenaar  * return TRUE if there is an abbreviation, FALSE if not
1398b66bab38SBram Moolenaar  */
1399b66bab38SBram Moolenaar     int
check_abbr(int c,char_u * ptr,int col,int mincol)1400b66bab38SBram Moolenaar check_abbr(
1401b66bab38SBram Moolenaar     int		c,
1402b66bab38SBram Moolenaar     char_u	*ptr,
1403b66bab38SBram Moolenaar     int		col,
1404b66bab38SBram Moolenaar     int		mincol)
1405b66bab38SBram Moolenaar {
1406b66bab38SBram Moolenaar     int		len;
1407b66bab38SBram Moolenaar     int		scol;		// starting column of the abbr.
1408b66bab38SBram Moolenaar     int		j;
1409b66bab38SBram Moolenaar     char_u	*s;
1410b66bab38SBram Moolenaar     char_u	tb[MB_MAXBYTES + 4];
1411b66bab38SBram Moolenaar     mapblock_T	*mp;
1412b66bab38SBram Moolenaar     mapblock_T	*mp2;
1413b66bab38SBram Moolenaar     int		clen = 0;	// length in characters
1414b66bab38SBram Moolenaar     int		is_id = TRUE;
1415b66bab38SBram Moolenaar     int		vim_abbr;
1416b66bab38SBram Moolenaar 
1417b66bab38SBram Moolenaar     if (typebuf.tb_no_abbr_cnt)	// abbrev. are not recursive
1418b66bab38SBram Moolenaar 	return FALSE;
1419b66bab38SBram Moolenaar 
1420b66bab38SBram Moolenaar     // no remapping implies no abbreviation, except for CTRL-]
1421b66bab38SBram Moolenaar     if (noremap_keys() && c != Ctrl_RSB)
1422b66bab38SBram Moolenaar 	return FALSE;
1423b66bab38SBram Moolenaar 
1424b66bab38SBram Moolenaar     // Check for word before the cursor: If it ends in a keyword char all
1425b66bab38SBram Moolenaar     // chars before it must be keyword chars or non-keyword chars, but not
1426b66bab38SBram Moolenaar     // white space. If it ends in a non-keyword char we accept any characters
1427b66bab38SBram Moolenaar     // before it except white space.
1428b66bab38SBram Moolenaar     if (col == 0)				// cannot be an abbr.
1429b66bab38SBram Moolenaar 	return FALSE;
1430b66bab38SBram Moolenaar 
1431b66bab38SBram Moolenaar     if (has_mbyte)
1432b66bab38SBram Moolenaar     {
1433b66bab38SBram Moolenaar 	char_u *p;
1434b66bab38SBram Moolenaar 
1435b66bab38SBram Moolenaar 	p = mb_prevptr(ptr, ptr + col);
1436b66bab38SBram Moolenaar 	if (!vim_iswordp(p))
1437b66bab38SBram Moolenaar 	    vim_abbr = TRUE;			// Vim added abbr.
1438b66bab38SBram Moolenaar 	else
1439b66bab38SBram Moolenaar 	{
1440b66bab38SBram Moolenaar 	    vim_abbr = FALSE;			// vi compatible abbr.
1441b66bab38SBram Moolenaar 	    if (p > ptr)
1442b66bab38SBram Moolenaar 		is_id = vim_iswordp(mb_prevptr(ptr, p));
1443b66bab38SBram Moolenaar 	}
1444b66bab38SBram Moolenaar 	clen = 1;
1445b66bab38SBram Moolenaar 	while (p > ptr + mincol)
1446b66bab38SBram Moolenaar 	{
1447b66bab38SBram Moolenaar 	    p = mb_prevptr(ptr, p);
1448b66bab38SBram Moolenaar 	    if (vim_isspace(*p) || (!vim_abbr && is_id != vim_iswordp(p)))
1449b66bab38SBram Moolenaar 	    {
1450b66bab38SBram Moolenaar 		p += (*mb_ptr2len)(p);
1451b66bab38SBram Moolenaar 		break;
1452b66bab38SBram Moolenaar 	    }
1453b66bab38SBram Moolenaar 	    ++clen;
1454b66bab38SBram Moolenaar 	}
1455b66bab38SBram Moolenaar 	scol = (int)(p - ptr);
1456b66bab38SBram Moolenaar     }
1457b66bab38SBram Moolenaar     else
1458b66bab38SBram Moolenaar     {
1459b66bab38SBram Moolenaar 	if (!vim_iswordc(ptr[col - 1]))
1460b66bab38SBram Moolenaar 	    vim_abbr = TRUE;			// Vim added abbr.
1461b66bab38SBram Moolenaar 	else
1462b66bab38SBram Moolenaar 	{
1463b66bab38SBram Moolenaar 	    vim_abbr = FALSE;			// vi compatible abbr.
1464b66bab38SBram Moolenaar 	    if (col > 1)
1465b66bab38SBram Moolenaar 		is_id = vim_iswordc(ptr[col - 2]);
1466b66bab38SBram Moolenaar 	}
1467b66bab38SBram Moolenaar 	for (scol = col - 1; scol > 0 && !vim_isspace(ptr[scol - 1])
1468b66bab38SBram Moolenaar 		&& (vim_abbr || is_id == vim_iswordc(ptr[scol - 1])); --scol)
1469b66bab38SBram Moolenaar 	    ;
1470b66bab38SBram Moolenaar     }
1471b66bab38SBram Moolenaar 
1472b66bab38SBram Moolenaar     if (scol < mincol)
1473b66bab38SBram Moolenaar 	scol = mincol;
1474b66bab38SBram Moolenaar     if (scol < col)		// there is a word in front of the cursor
1475b66bab38SBram Moolenaar     {
1476b66bab38SBram Moolenaar 	ptr += scol;
1477b66bab38SBram Moolenaar 	len = col - scol;
1478b66bab38SBram Moolenaar 	mp = curbuf->b_first_abbr;
1479b66bab38SBram Moolenaar 	mp2 = first_abbr;
1480b66bab38SBram Moolenaar 	if (mp == NULL)
1481b66bab38SBram Moolenaar 	{
1482b66bab38SBram Moolenaar 	    mp = mp2;
1483b66bab38SBram Moolenaar 	    mp2 = NULL;
1484b66bab38SBram Moolenaar 	}
1485b66bab38SBram Moolenaar 	for ( ; mp; mp->m_next == NULL
1486b66bab38SBram Moolenaar 				  ? (mp = mp2, mp2 = NULL) : (mp = mp->m_next))
1487b66bab38SBram Moolenaar 	{
1488b66bab38SBram Moolenaar 	    int		qlen = mp->m_keylen;
1489b66bab38SBram Moolenaar 	    char_u	*q = mp->m_keys;
1490b66bab38SBram Moolenaar 	    int		match;
1491b66bab38SBram Moolenaar 
1492b66bab38SBram Moolenaar 	    if (vim_strbyte(mp->m_keys, K_SPECIAL) != NULL)
1493b66bab38SBram Moolenaar 	    {
1494b66bab38SBram Moolenaar 		char_u *qe = vim_strsave(mp->m_keys);
1495b66bab38SBram Moolenaar 
1496b66bab38SBram Moolenaar 		// might have CSI escaped mp->m_keys
1497b66bab38SBram Moolenaar 		if (qe != NULL)
1498b66bab38SBram Moolenaar 		{
1499b66bab38SBram Moolenaar 		    q = qe;
1500b66bab38SBram Moolenaar 		    vim_unescape_csi(q);
1501b66bab38SBram Moolenaar 		    qlen = (int)STRLEN(q);
1502b66bab38SBram Moolenaar 		}
1503b66bab38SBram Moolenaar 	    }
1504b66bab38SBram Moolenaar 
1505b66bab38SBram Moolenaar 	    // find entries with right mode and keys
1506b66bab38SBram Moolenaar 	    match =    (mp->m_mode & State)
1507b66bab38SBram Moolenaar 		    && qlen == len
1508b66bab38SBram Moolenaar 		    && !STRNCMP(q, ptr, (size_t)len);
1509b66bab38SBram Moolenaar 	    if (q != mp->m_keys)
1510b66bab38SBram Moolenaar 		vim_free(q);
1511b66bab38SBram Moolenaar 	    if (match)
1512b66bab38SBram Moolenaar 		break;
1513b66bab38SBram Moolenaar 	}
1514b66bab38SBram Moolenaar 	if (mp != NULL)
1515b66bab38SBram Moolenaar 	{
1516b66bab38SBram Moolenaar 	    // Found a match:
1517b66bab38SBram Moolenaar 	    // Insert the rest of the abbreviation in typebuf.tb_buf[].
1518b66bab38SBram Moolenaar 	    // This goes from end to start.
1519b66bab38SBram Moolenaar 	    //
1520b66bab38SBram Moolenaar 	    // Characters 0x000 - 0x100: normal chars, may need CTRL-V,
1521b66bab38SBram Moolenaar 	    // except K_SPECIAL: Becomes K_SPECIAL KS_SPECIAL KE_FILLER
1522b66bab38SBram Moolenaar 	    // Characters where IS_SPECIAL() == TRUE: key codes, need
1523b66bab38SBram Moolenaar 	    // K_SPECIAL. Other characters (with ABBR_OFF): don't use CTRL-V.
1524b66bab38SBram Moolenaar 	    //
1525b66bab38SBram Moolenaar 	    // Character CTRL-] is treated specially - it completes the
1526b66bab38SBram Moolenaar 	    // abbreviation, but is not inserted into the input stream.
1527b66bab38SBram Moolenaar 	    j = 0;
1528b66bab38SBram Moolenaar 	    if (c != Ctrl_RSB)
1529b66bab38SBram Moolenaar 	    {
1530b66bab38SBram Moolenaar 					// special key code, split up
1531b66bab38SBram Moolenaar 		if (IS_SPECIAL(c) || c == K_SPECIAL)
1532b66bab38SBram Moolenaar 		{
1533b66bab38SBram Moolenaar 		    tb[j++] = K_SPECIAL;
1534b66bab38SBram Moolenaar 		    tb[j++] = K_SECOND(c);
1535b66bab38SBram Moolenaar 		    tb[j++] = K_THIRD(c);
1536b66bab38SBram Moolenaar 		}
1537b66bab38SBram Moolenaar 		else
1538b66bab38SBram Moolenaar 		{
1539b66bab38SBram Moolenaar 		    if (c < ABBR_OFF && (c < ' ' || c > '~'))
1540b66bab38SBram Moolenaar 			tb[j++] = Ctrl_V;	// special char needs CTRL-V
1541b66bab38SBram Moolenaar 		    if (has_mbyte)
1542b66bab38SBram Moolenaar 		    {
15434934ed34SBram Moolenaar 			int	newlen;
15444934ed34SBram Moolenaar 			char_u	*escaped;
15454934ed34SBram Moolenaar 
1546b66bab38SBram Moolenaar 			// if ABBR_OFF has been added, remove it here
1547b66bab38SBram Moolenaar 			if (c >= ABBR_OFF)
1548b66bab38SBram Moolenaar 			    c -= ABBR_OFF;
15494934ed34SBram Moolenaar 			newlen = (*mb_char2bytes)(c, tb + j);
15504934ed34SBram Moolenaar 			tb[j + newlen] = NUL;
15514934ed34SBram Moolenaar 			// Need to escape K_SPECIAL.
15524934ed34SBram Moolenaar 			escaped = vim_strsave_escape_csi(tb + j);
15534934ed34SBram Moolenaar 			if (escaped != NULL)
15544934ed34SBram Moolenaar 			{
1555551c1aedSBram Moolenaar 			    newlen = (int)STRLEN(escaped);
15564934ed34SBram Moolenaar 			    mch_memmove(tb + j, escaped, newlen);
15574934ed34SBram Moolenaar 			    j += newlen;
15584934ed34SBram Moolenaar 			    vim_free(escaped);
15594934ed34SBram Moolenaar 			}
1560b66bab38SBram Moolenaar 		    }
1561b66bab38SBram Moolenaar 		    else
1562b66bab38SBram Moolenaar 			tb[j++] = c;
1563b66bab38SBram Moolenaar 		}
1564b66bab38SBram Moolenaar 		tb[j] = NUL;
1565b66bab38SBram Moolenaar 					// insert the last typed char
1566b66bab38SBram Moolenaar 		(void)ins_typebuf(tb, 1, 0, TRUE, mp->m_silent);
1567b66bab38SBram Moolenaar 	    }
1568b66bab38SBram Moolenaar #ifdef FEAT_EVAL
1569b66bab38SBram Moolenaar 	    if (mp->m_expr)
1570b66bab38SBram Moolenaar 		s = eval_map_expr(mp->m_str, c);
1571b66bab38SBram Moolenaar 	    else
1572b66bab38SBram Moolenaar #endif
1573b66bab38SBram Moolenaar 		s = mp->m_str;
1574b66bab38SBram Moolenaar 	    if (s != NULL)
1575b66bab38SBram Moolenaar 	    {
1576b66bab38SBram Moolenaar 					// insert the to string
1577b66bab38SBram Moolenaar 		(void)ins_typebuf(s, mp->m_noremap, 0, TRUE, mp->m_silent);
1578b66bab38SBram Moolenaar 					// no abbrev. for these chars
1579b66bab38SBram Moolenaar 		typebuf.tb_no_abbr_cnt += (int)STRLEN(s) + j + 1;
1580b66bab38SBram Moolenaar #ifdef FEAT_EVAL
1581b66bab38SBram Moolenaar 		if (mp->m_expr)
1582b66bab38SBram Moolenaar 		    vim_free(s);
1583b66bab38SBram Moolenaar #endif
1584b66bab38SBram Moolenaar 	    }
1585b66bab38SBram Moolenaar 
1586b66bab38SBram Moolenaar 	    tb[0] = Ctrl_H;
1587b66bab38SBram Moolenaar 	    tb[1] = NUL;
1588b66bab38SBram Moolenaar 	    if (has_mbyte)
1589b66bab38SBram Moolenaar 		len = clen;	// Delete characters instead of bytes
1590b66bab38SBram Moolenaar 	    while (len-- > 0)		// delete the from string
1591b66bab38SBram Moolenaar 		(void)ins_typebuf(tb, 1, 0, TRUE, mp->m_silent);
1592b66bab38SBram Moolenaar 	    return TRUE;
1593b66bab38SBram Moolenaar 	}
1594b66bab38SBram Moolenaar     }
1595b66bab38SBram Moolenaar     return FALSE;
1596b66bab38SBram Moolenaar }
1597b66bab38SBram Moolenaar 
1598b66bab38SBram Moolenaar #ifdef FEAT_EVAL
1599b66bab38SBram Moolenaar /*
1600b66bab38SBram Moolenaar  * Evaluate the RHS of a mapping or abbreviations and take care of escaping
1601b66bab38SBram Moolenaar  * special characters.
1602b66bab38SBram Moolenaar  */
1603b66bab38SBram Moolenaar     char_u *
eval_map_expr(char_u * str,int c)1604b66bab38SBram Moolenaar eval_map_expr(
1605b66bab38SBram Moolenaar     char_u	*str,
1606b66bab38SBram Moolenaar     int		c)	    // NUL or typed character for abbreviation
1607b66bab38SBram Moolenaar {
1608b66bab38SBram Moolenaar     char_u	*res;
1609b66bab38SBram Moolenaar     char_u	*p;
1610b66bab38SBram Moolenaar     char_u	*expr;
1611b66bab38SBram Moolenaar     pos_T	save_cursor;
1612b66bab38SBram Moolenaar     int		save_msg_col;
1613b66bab38SBram Moolenaar     int		save_msg_row;
1614b66bab38SBram Moolenaar 
1615b66bab38SBram Moolenaar     // Remove escaping of CSI, because "str" is in a format to be used as
1616b66bab38SBram Moolenaar     // typeahead.
1617b66bab38SBram Moolenaar     expr = vim_strsave(str);
1618b66bab38SBram Moolenaar     if (expr == NULL)
1619b66bab38SBram Moolenaar 	return NULL;
1620b66bab38SBram Moolenaar     vim_unescape_csi(expr);
1621b66bab38SBram Moolenaar 
1622b66bab38SBram Moolenaar     // Forbid changing text or using ":normal" to avoid most of the bad side
1623b66bab38SBram Moolenaar     // effects.  Also restore the cursor position.
16246adb9ea0SBram Moolenaar     ++textwinlock;
1625b66bab38SBram Moolenaar     ++ex_normal_lock;
1626b66bab38SBram Moolenaar     set_vim_var_char(c);  // set v:char to the typed character
1627b66bab38SBram Moolenaar     save_cursor = curwin->w_cursor;
1628b66bab38SBram Moolenaar     save_msg_col = msg_col;
1629b66bab38SBram Moolenaar     save_msg_row = msg_row;
1630b171fb17SBram Moolenaar     p = eval_to_string(expr, FALSE);
16316adb9ea0SBram Moolenaar     --textwinlock;
1632b66bab38SBram Moolenaar     --ex_normal_lock;
1633b66bab38SBram Moolenaar     curwin->w_cursor = save_cursor;
1634b66bab38SBram Moolenaar     msg_col = save_msg_col;
1635b66bab38SBram Moolenaar     msg_row = save_msg_row;
1636b66bab38SBram Moolenaar 
1637b66bab38SBram Moolenaar     vim_free(expr);
1638b66bab38SBram Moolenaar 
1639b66bab38SBram Moolenaar     if (p == NULL)
1640b66bab38SBram Moolenaar 	return NULL;
1641b66bab38SBram Moolenaar     // Escape CSI in the result to be able to use the string as typeahead.
1642b66bab38SBram Moolenaar     res = vim_strsave_escape_csi(p);
1643b66bab38SBram Moolenaar     vim_free(p);
1644b66bab38SBram Moolenaar 
1645b66bab38SBram Moolenaar     return res;
1646b66bab38SBram Moolenaar }
1647b66bab38SBram Moolenaar #endif
1648b66bab38SBram Moolenaar 
1649b66bab38SBram Moolenaar /*
1650b66bab38SBram Moolenaar  * Copy "p" to allocated memory, escaping K_SPECIAL and CSI so that the result
1651b66bab38SBram Moolenaar  * can be put in the typeahead buffer.
1652b66bab38SBram Moolenaar  * Returns NULL when out of memory.
1653b66bab38SBram Moolenaar  */
1654b66bab38SBram Moolenaar     char_u *
vim_strsave_escape_csi(char_u * p)1655957cf67dSBram Moolenaar vim_strsave_escape_csi(char_u *p)
1656b66bab38SBram Moolenaar {
1657b66bab38SBram Moolenaar     char_u	*res;
1658b66bab38SBram Moolenaar     char_u	*s, *d;
1659b66bab38SBram Moolenaar 
1660b66bab38SBram Moolenaar     // Need a buffer to hold up to three times as much.  Four in case of an
1661b66bab38SBram Moolenaar     // illegal utf-8 byte:
1662b66bab38SBram Moolenaar     // 0xc0 -> 0xc3 0x80 -> 0xc3 K_SPECIAL KS_SPECIAL KE_FILLER
1663b66bab38SBram Moolenaar     res = alloc(STRLEN(p) * 4 + 1);
1664b66bab38SBram Moolenaar     if (res != NULL)
1665b66bab38SBram Moolenaar     {
1666b66bab38SBram Moolenaar 	d = res;
1667b66bab38SBram Moolenaar 	for (s = p; *s != NUL; )
1668b66bab38SBram Moolenaar 	{
1669b66bab38SBram Moolenaar 	    if (s[0] == K_SPECIAL && s[1] != NUL && s[2] != NUL)
1670b66bab38SBram Moolenaar 	    {
1671b66bab38SBram Moolenaar 		// Copy special key unmodified.
1672b66bab38SBram Moolenaar 		*d++ = *s++;
1673b66bab38SBram Moolenaar 		*d++ = *s++;
1674b66bab38SBram Moolenaar 		*d++ = *s++;
1675b66bab38SBram Moolenaar 	    }
1676b66bab38SBram Moolenaar 	    else
1677b66bab38SBram Moolenaar 	    {
1678b66bab38SBram Moolenaar 		// Add character, possibly multi-byte to destination, escaping
1679b66bab38SBram Moolenaar 		// CSI and K_SPECIAL. Be careful, it can be an illegal byte!
1680b66bab38SBram Moolenaar 		d = add_char2buf(PTR2CHAR(s), d);
1681b66bab38SBram Moolenaar 		s += MB_CPTR2LEN(s);
1682b66bab38SBram Moolenaar 	    }
1683b66bab38SBram Moolenaar 	}
1684b66bab38SBram Moolenaar 	*d = NUL;
1685b66bab38SBram Moolenaar     }
1686b66bab38SBram Moolenaar     return res;
1687b66bab38SBram Moolenaar }
1688b66bab38SBram Moolenaar 
1689b66bab38SBram Moolenaar /*
1690b66bab38SBram Moolenaar  * Remove escaping from CSI and K_SPECIAL characters.  Reverse of
1691b66bab38SBram Moolenaar  * vim_strsave_escape_csi().  Works in-place.
1692b66bab38SBram Moolenaar  */
1693b66bab38SBram Moolenaar     void
vim_unescape_csi(char_u * p)1694b66bab38SBram Moolenaar vim_unescape_csi(char_u *p)
1695b66bab38SBram Moolenaar {
1696b66bab38SBram Moolenaar     char_u	*s = p, *d = p;
1697b66bab38SBram Moolenaar 
1698b66bab38SBram Moolenaar     while (*s != NUL)
1699b66bab38SBram Moolenaar     {
1700b66bab38SBram Moolenaar 	if (s[0] == K_SPECIAL && s[1] == KS_SPECIAL && s[2] == KE_FILLER)
1701b66bab38SBram Moolenaar 	{
1702b66bab38SBram Moolenaar 	    *d++ = K_SPECIAL;
1703b66bab38SBram Moolenaar 	    s += 3;
1704b66bab38SBram Moolenaar 	}
1705b66bab38SBram Moolenaar 	else if ((s[0] == K_SPECIAL || s[0] == CSI)
1706b66bab38SBram Moolenaar 				   && s[1] == KS_EXTRA && s[2] == (int)KE_CSI)
1707b66bab38SBram Moolenaar 	{
1708b66bab38SBram Moolenaar 	    *d++ = CSI;
1709b66bab38SBram Moolenaar 	    s += 3;
1710b66bab38SBram Moolenaar 	}
1711b66bab38SBram Moolenaar 	else
1712b66bab38SBram Moolenaar 	    *d++ = *s++;
1713b66bab38SBram Moolenaar     }
1714b66bab38SBram Moolenaar     *d = NUL;
1715b66bab38SBram Moolenaar }
1716b66bab38SBram Moolenaar 
1717b66bab38SBram Moolenaar /*
1718b66bab38SBram Moolenaar  * Write map commands for the current mappings to an .exrc file.
1719b66bab38SBram Moolenaar  * Return FAIL on error, OK otherwise.
1720b66bab38SBram Moolenaar  */
1721b66bab38SBram Moolenaar     int
makemap(FILE * fd,buf_T * buf)1722b66bab38SBram Moolenaar makemap(
1723b66bab38SBram Moolenaar     FILE	*fd,
1724b66bab38SBram Moolenaar     buf_T	*buf)	    // buffer for local mappings or NULL
1725b66bab38SBram Moolenaar {
1726b66bab38SBram Moolenaar     mapblock_T	*mp;
1727b66bab38SBram Moolenaar     char_u	c1, c2, c3;
1728b66bab38SBram Moolenaar     char_u	*p;
1729b66bab38SBram Moolenaar     char	*cmd;
1730b66bab38SBram Moolenaar     int		abbr;
1731b66bab38SBram Moolenaar     int		hash;
1732b66bab38SBram Moolenaar     int		did_cpo = FALSE;
1733b66bab38SBram Moolenaar     int		i;
1734b66bab38SBram Moolenaar 
1735b66bab38SBram Moolenaar     validate_maphash();
1736b66bab38SBram Moolenaar 
1737b66bab38SBram Moolenaar     // Do the loop twice: Once for mappings, once for abbreviations.
1738b66bab38SBram Moolenaar     // Then loop over all map hash lists.
1739b66bab38SBram Moolenaar     for (abbr = 0; abbr < 2; ++abbr)
1740b66bab38SBram Moolenaar 	for (hash = 0; hash < 256; ++hash)
1741b66bab38SBram Moolenaar 	{
1742b66bab38SBram Moolenaar 	    if (abbr)
1743b66bab38SBram Moolenaar 	    {
1744b66bab38SBram Moolenaar 		if (hash > 0)		// there is only one abbr list
1745b66bab38SBram Moolenaar 		    break;
1746b66bab38SBram Moolenaar 		if (buf != NULL)
1747b66bab38SBram Moolenaar 		    mp = buf->b_first_abbr;
1748b66bab38SBram Moolenaar 		else
1749b66bab38SBram Moolenaar 		    mp = first_abbr;
1750b66bab38SBram Moolenaar 	    }
1751b66bab38SBram Moolenaar 	    else
1752b66bab38SBram Moolenaar 	    {
1753b66bab38SBram Moolenaar 		if (buf != NULL)
1754b66bab38SBram Moolenaar 		    mp = buf->b_maphash[hash];
1755b66bab38SBram Moolenaar 		else
1756b66bab38SBram Moolenaar 		    mp = maphash[hash];
1757b66bab38SBram Moolenaar 	    }
1758b66bab38SBram Moolenaar 
1759b66bab38SBram Moolenaar 	    for ( ; mp; mp = mp->m_next)
1760b66bab38SBram Moolenaar 	    {
1761b66bab38SBram Moolenaar 		// skip script-local mappings
1762b66bab38SBram Moolenaar 		if (mp->m_noremap == REMAP_SCRIPT)
1763b66bab38SBram Moolenaar 		    continue;
1764b66bab38SBram Moolenaar 
1765b66bab38SBram Moolenaar 		// skip mappings that contain a <SNR> (script-local thing),
1766b66bab38SBram Moolenaar 		// they probably don't work when loaded again
1767b66bab38SBram Moolenaar 		for (p = mp->m_str; *p != NUL; ++p)
1768b66bab38SBram Moolenaar 		    if (p[0] == K_SPECIAL && p[1] == KS_EXTRA
1769b66bab38SBram Moolenaar 						       && p[2] == (int)KE_SNR)
1770b66bab38SBram Moolenaar 			break;
1771b66bab38SBram Moolenaar 		if (*p != NUL)
1772b66bab38SBram Moolenaar 		    continue;
1773b66bab38SBram Moolenaar 
1774b66bab38SBram Moolenaar 		// It's possible to create a mapping and then ":unmap" certain
1775b66bab38SBram Moolenaar 		// modes.  We recreate this here by mapping the individual
1776b66bab38SBram Moolenaar 		// modes, which requires up to three of them.
1777b66bab38SBram Moolenaar 		c1 = NUL;
1778b66bab38SBram Moolenaar 		c2 = NUL;
1779b66bab38SBram Moolenaar 		c3 = NUL;
1780b66bab38SBram Moolenaar 		if (abbr)
1781b66bab38SBram Moolenaar 		    cmd = "abbr";
1782b66bab38SBram Moolenaar 		else
1783b66bab38SBram Moolenaar 		    cmd = "map";
1784b66bab38SBram Moolenaar 		switch (mp->m_mode)
1785b66bab38SBram Moolenaar 		{
1786b66bab38SBram Moolenaar 		    case NORMAL + VISUAL + SELECTMODE + OP_PENDING:
1787b66bab38SBram Moolenaar 			break;
1788b66bab38SBram Moolenaar 		    case NORMAL:
1789b66bab38SBram Moolenaar 			c1 = 'n';
1790b66bab38SBram Moolenaar 			break;
1791b66bab38SBram Moolenaar 		    case VISUAL:
1792b66bab38SBram Moolenaar 			c1 = 'x';
1793b66bab38SBram Moolenaar 			break;
1794b66bab38SBram Moolenaar 		    case SELECTMODE:
1795b66bab38SBram Moolenaar 			c1 = 's';
1796b66bab38SBram Moolenaar 			break;
1797b66bab38SBram Moolenaar 		    case OP_PENDING:
1798b66bab38SBram Moolenaar 			c1 = 'o';
1799b66bab38SBram Moolenaar 			break;
1800b66bab38SBram Moolenaar 		    case NORMAL + VISUAL:
1801b66bab38SBram Moolenaar 			c1 = 'n';
1802b66bab38SBram Moolenaar 			c2 = 'x';
1803b66bab38SBram Moolenaar 			break;
1804b66bab38SBram Moolenaar 		    case NORMAL + SELECTMODE:
1805b66bab38SBram Moolenaar 			c1 = 'n';
1806b66bab38SBram Moolenaar 			c2 = 's';
1807b66bab38SBram Moolenaar 			break;
1808b66bab38SBram Moolenaar 		    case NORMAL + OP_PENDING:
1809b66bab38SBram Moolenaar 			c1 = 'n';
1810b66bab38SBram Moolenaar 			c2 = 'o';
1811b66bab38SBram Moolenaar 			break;
1812b66bab38SBram Moolenaar 		    case VISUAL + SELECTMODE:
1813b66bab38SBram Moolenaar 			c1 = 'v';
1814b66bab38SBram Moolenaar 			break;
1815b66bab38SBram Moolenaar 		    case VISUAL + OP_PENDING:
1816b66bab38SBram Moolenaar 			c1 = 'x';
1817b66bab38SBram Moolenaar 			c2 = 'o';
1818b66bab38SBram Moolenaar 			break;
1819b66bab38SBram Moolenaar 		    case SELECTMODE + OP_PENDING:
1820b66bab38SBram Moolenaar 			c1 = 's';
1821b66bab38SBram Moolenaar 			c2 = 'o';
1822b66bab38SBram Moolenaar 			break;
1823b66bab38SBram Moolenaar 		    case NORMAL + VISUAL + SELECTMODE:
1824b66bab38SBram Moolenaar 			c1 = 'n';
1825b66bab38SBram Moolenaar 			c2 = 'v';
1826b66bab38SBram Moolenaar 			break;
1827b66bab38SBram Moolenaar 		    case NORMAL + VISUAL + OP_PENDING:
1828b66bab38SBram Moolenaar 			c1 = 'n';
1829b66bab38SBram Moolenaar 			c2 = 'x';
1830b66bab38SBram Moolenaar 			c3 = 'o';
1831b66bab38SBram Moolenaar 			break;
1832b66bab38SBram Moolenaar 		    case NORMAL + SELECTMODE + OP_PENDING:
1833b66bab38SBram Moolenaar 			c1 = 'n';
1834b66bab38SBram Moolenaar 			c2 = 's';
1835b66bab38SBram Moolenaar 			c3 = 'o';
1836b66bab38SBram Moolenaar 			break;
1837b66bab38SBram Moolenaar 		    case VISUAL + SELECTMODE + OP_PENDING:
1838b66bab38SBram Moolenaar 			c1 = 'v';
1839b66bab38SBram Moolenaar 			c2 = 'o';
1840b66bab38SBram Moolenaar 			break;
1841b66bab38SBram Moolenaar 		    case CMDLINE + INSERT:
1842b66bab38SBram Moolenaar 			if (!abbr)
1843b66bab38SBram Moolenaar 			    cmd = "map!";
1844b66bab38SBram Moolenaar 			break;
1845b66bab38SBram Moolenaar 		    case CMDLINE:
1846b66bab38SBram Moolenaar 			c1 = 'c';
1847b66bab38SBram Moolenaar 			break;
1848b66bab38SBram Moolenaar 		    case INSERT:
1849b66bab38SBram Moolenaar 			c1 = 'i';
1850b66bab38SBram Moolenaar 			break;
1851b66bab38SBram Moolenaar 		    case LANGMAP:
1852b66bab38SBram Moolenaar 			c1 = 'l';
1853b66bab38SBram Moolenaar 			break;
1854b66bab38SBram Moolenaar 		    case TERMINAL:
1855b66bab38SBram Moolenaar 			c1 = 't';
1856b66bab38SBram Moolenaar 			break;
1857b66bab38SBram Moolenaar 		    default:
1858b66bab38SBram Moolenaar 			iemsg(_("E228: makemap: Illegal mode"));
1859b66bab38SBram Moolenaar 			return FAIL;
1860b66bab38SBram Moolenaar 		}
1861b66bab38SBram Moolenaar 		do	// do this twice if c2 is set, 3 times with c3
1862b66bab38SBram Moolenaar 		{
1863b66bab38SBram Moolenaar 		    // When outputting <> form, need to make sure that 'cpo'
1864b66bab38SBram Moolenaar 		    // is set to the Vim default.
1865b66bab38SBram Moolenaar 		    if (!did_cpo)
1866b66bab38SBram Moolenaar 		    {
1867b66bab38SBram Moolenaar 			if (*mp->m_str == NUL)		// will use <Nop>
1868b66bab38SBram Moolenaar 			    did_cpo = TRUE;
1869b66bab38SBram Moolenaar 			else
1870b66bab38SBram Moolenaar 			    for (i = 0; i < 2; ++i)
1871b66bab38SBram Moolenaar 				for (p = (i ? mp->m_str : mp->m_keys); *p; ++p)
1872b66bab38SBram Moolenaar 				    if (*p == K_SPECIAL || *p == NL)
1873b66bab38SBram Moolenaar 					did_cpo = TRUE;
1874b66bab38SBram Moolenaar 			if (did_cpo)
1875b66bab38SBram Moolenaar 			{
1876b66bab38SBram Moolenaar 			    if (fprintf(fd, "let s:cpo_save=&cpo") < 0
1877b66bab38SBram Moolenaar 				    || put_eol(fd) < 0
1878b66bab38SBram Moolenaar 				    || fprintf(fd, "set cpo&vim") < 0
1879b66bab38SBram Moolenaar 				    || put_eol(fd) < 0)
1880b66bab38SBram Moolenaar 				return FAIL;
1881b66bab38SBram Moolenaar 			}
1882b66bab38SBram Moolenaar 		    }
1883b66bab38SBram Moolenaar 		    if (c1 && putc(c1, fd) < 0)
1884b66bab38SBram Moolenaar 			return FAIL;
1885b66bab38SBram Moolenaar 		    if (mp->m_noremap != REMAP_YES && fprintf(fd, "nore") < 0)
1886b66bab38SBram Moolenaar 			return FAIL;
1887b66bab38SBram Moolenaar 		    if (fputs(cmd, fd) < 0)
1888b66bab38SBram Moolenaar 			return FAIL;
1889b66bab38SBram Moolenaar 		    if (buf != NULL && fputs(" <buffer>", fd) < 0)
1890b66bab38SBram Moolenaar 			return FAIL;
1891b66bab38SBram Moolenaar 		    if (mp->m_nowait && fputs(" <nowait>", fd) < 0)
1892b66bab38SBram Moolenaar 			return FAIL;
1893b66bab38SBram Moolenaar 		    if (mp->m_silent && fputs(" <silent>", fd) < 0)
1894b66bab38SBram Moolenaar 			return FAIL;
1895b66bab38SBram Moolenaar #ifdef FEAT_EVAL
1896b66bab38SBram Moolenaar 		    if (mp->m_noremap == REMAP_SCRIPT
1897b66bab38SBram Moolenaar 						 && fputs("<script>", fd) < 0)
1898b66bab38SBram Moolenaar 			return FAIL;
1899b66bab38SBram Moolenaar 		    if (mp->m_expr && fputs(" <expr>", fd) < 0)
1900b66bab38SBram Moolenaar 			return FAIL;
1901b66bab38SBram Moolenaar #endif
1902b66bab38SBram Moolenaar 
1903b66bab38SBram Moolenaar 		    if (       putc(' ', fd) < 0
1904b66bab38SBram Moolenaar 			    || put_escstr(fd, mp->m_keys, 0) == FAIL
1905b66bab38SBram Moolenaar 			    || putc(' ', fd) < 0
1906b66bab38SBram Moolenaar 			    || put_escstr(fd, mp->m_str, 1) == FAIL
1907b66bab38SBram Moolenaar 			    || put_eol(fd) < 0)
1908b66bab38SBram Moolenaar 			return FAIL;
1909b66bab38SBram Moolenaar 		    c1 = c2;
1910b66bab38SBram Moolenaar 		    c2 = c3;
1911b66bab38SBram Moolenaar 		    c3 = NUL;
1912b66bab38SBram Moolenaar 		} while (c1 != NUL);
1913b66bab38SBram Moolenaar 	    }
1914b66bab38SBram Moolenaar 	}
1915b66bab38SBram Moolenaar 
1916b66bab38SBram Moolenaar     if (did_cpo)
1917b66bab38SBram Moolenaar 	if (fprintf(fd, "let &cpo=s:cpo_save") < 0
1918b66bab38SBram Moolenaar 		|| put_eol(fd) < 0
1919b66bab38SBram Moolenaar 		|| fprintf(fd, "unlet s:cpo_save") < 0
1920b66bab38SBram Moolenaar 		|| put_eol(fd) < 0)
1921b66bab38SBram Moolenaar 	    return FAIL;
1922b66bab38SBram Moolenaar     return OK;
1923b66bab38SBram Moolenaar }
1924b66bab38SBram Moolenaar 
1925b66bab38SBram Moolenaar /*
1926b66bab38SBram Moolenaar  * write escape string to file
1927b66bab38SBram Moolenaar  * "what": 0 for :map lhs, 1 for :map rhs, 2 for :set
1928b66bab38SBram Moolenaar  *
1929b66bab38SBram Moolenaar  * return FAIL for failure, OK otherwise
1930b66bab38SBram Moolenaar  */
1931b66bab38SBram Moolenaar     int
put_escstr(FILE * fd,char_u * strstart,int what)1932b66bab38SBram Moolenaar put_escstr(FILE *fd, char_u *strstart, int what)
1933b66bab38SBram Moolenaar {
1934b66bab38SBram Moolenaar     char_u	*str = strstart;
1935b66bab38SBram Moolenaar     int		c;
1936b66bab38SBram Moolenaar     int		modifiers;
1937b66bab38SBram Moolenaar 
1938b66bab38SBram Moolenaar     // :map xx <Nop>
1939b66bab38SBram Moolenaar     if (*str == NUL && what == 1)
1940b66bab38SBram Moolenaar     {
1941b66bab38SBram Moolenaar 	if (fprintf(fd, "<Nop>") < 0)
1942b66bab38SBram Moolenaar 	    return FAIL;
1943b66bab38SBram Moolenaar 	return OK;
1944b66bab38SBram Moolenaar     }
1945b66bab38SBram Moolenaar 
1946b66bab38SBram Moolenaar     for ( ; *str != NUL; ++str)
1947b66bab38SBram Moolenaar     {
1948b66bab38SBram Moolenaar 	char_u	*p;
1949b66bab38SBram Moolenaar 
1950b66bab38SBram Moolenaar 	// Check for a multi-byte character, which may contain escaped
1951b66bab38SBram Moolenaar 	// K_SPECIAL and CSI bytes
1952b66bab38SBram Moolenaar 	p = mb_unescape(&str);
1953b66bab38SBram Moolenaar 	if (p != NULL)
1954b66bab38SBram Moolenaar 	{
1955b66bab38SBram Moolenaar 	    while (*p != NUL)
1956b66bab38SBram Moolenaar 		if (fputc(*p++, fd) < 0)
1957b66bab38SBram Moolenaar 		    return FAIL;
1958b66bab38SBram Moolenaar 	    --str;
1959b66bab38SBram Moolenaar 	    continue;
1960b66bab38SBram Moolenaar 	}
1961b66bab38SBram Moolenaar 
1962b66bab38SBram Moolenaar 	c = *str;
1963b66bab38SBram Moolenaar 	// Special key codes have to be translated to be able to make sense
1964b66bab38SBram Moolenaar 	// when they are read back.
1965b66bab38SBram Moolenaar 	if (c == K_SPECIAL && what != 2)
1966b66bab38SBram Moolenaar 	{
196702c037a4SBram Moolenaar 	    modifiers = 0;
1968b66bab38SBram Moolenaar 	    if (str[1] == KS_MODIFIER)
1969b66bab38SBram Moolenaar 	    {
1970b66bab38SBram Moolenaar 		modifiers = str[2];
1971b66bab38SBram Moolenaar 		str += 3;
1972b66bab38SBram Moolenaar 		c = *str;
1973b66bab38SBram Moolenaar 	    }
1974b66bab38SBram Moolenaar 	    if (c == K_SPECIAL)
1975b66bab38SBram Moolenaar 	    {
1976b66bab38SBram Moolenaar 		c = TO_SPECIAL(str[1], str[2]);
1977b66bab38SBram Moolenaar 		str += 2;
1978b66bab38SBram Moolenaar 	    }
1979b66bab38SBram Moolenaar 	    if (IS_SPECIAL(c) || modifiers)	// special key
1980b66bab38SBram Moolenaar 	    {
1981b66bab38SBram Moolenaar 		if (fputs((char *)get_special_key_name(c, modifiers), fd) < 0)
1982b66bab38SBram Moolenaar 		    return FAIL;
1983b66bab38SBram Moolenaar 		continue;
1984b66bab38SBram Moolenaar 	    }
1985b66bab38SBram Moolenaar 	}
1986b66bab38SBram Moolenaar 
1987b66bab38SBram Moolenaar 	// A '\n' in a map command should be written as <NL>.
1988b66bab38SBram Moolenaar 	// A '\n' in a set command should be written as \^V^J.
1989b66bab38SBram Moolenaar 	if (c == NL)
1990b66bab38SBram Moolenaar 	{
1991b66bab38SBram Moolenaar 	    if (what == 2)
1992b66bab38SBram Moolenaar 	    {
1993b66bab38SBram Moolenaar 		if (fprintf(fd, IF_EB("\\\026\n", "\\" CTRL_V_STR "\n")) < 0)
1994b66bab38SBram Moolenaar 		    return FAIL;
1995b66bab38SBram Moolenaar 	    }
1996b66bab38SBram Moolenaar 	    else
1997b66bab38SBram Moolenaar 	    {
1998b66bab38SBram Moolenaar 		if (fprintf(fd, "<NL>") < 0)
1999b66bab38SBram Moolenaar 		    return FAIL;
2000b66bab38SBram Moolenaar 	    }
2001b66bab38SBram Moolenaar 	    continue;
2002b66bab38SBram Moolenaar 	}
2003b66bab38SBram Moolenaar 
2004b66bab38SBram Moolenaar 	// Some characters have to be escaped with CTRL-V to
2005b66bab38SBram Moolenaar 	// prevent them from misinterpreted in DoOneCmd().
2006b66bab38SBram Moolenaar 	// A space, Tab and '"' has to be escaped with a backslash to
2007b66bab38SBram Moolenaar 	// prevent it to be misinterpreted in do_set().
2008b66bab38SBram Moolenaar 	// A space has to be escaped with a CTRL-V when it's at the start of a
2009b66bab38SBram Moolenaar 	// ":map" rhs.
2010b66bab38SBram Moolenaar 	// A '<' has to be escaped with a CTRL-V to prevent it being
2011b66bab38SBram Moolenaar 	// interpreted as the start of a special key name.
2012b66bab38SBram Moolenaar 	// A space in the lhs of a :map needs a CTRL-V.
2013b66bab38SBram Moolenaar 	if (what == 2 && (VIM_ISWHITE(c) || c == '"' || c == '\\'))
2014b66bab38SBram Moolenaar 	{
2015b66bab38SBram Moolenaar 	    if (putc('\\', fd) < 0)
2016b66bab38SBram Moolenaar 		return FAIL;
2017b66bab38SBram Moolenaar 	}
2018b66bab38SBram Moolenaar 	else if (c < ' ' || c > '~' || c == '|'
2019b66bab38SBram Moolenaar 		|| (what == 0 && c == ' ')
2020b66bab38SBram Moolenaar 		|| (what == 1 && str == strstart && c == ' ')
2021b66bab38SBram Moolenaar 		|| (what != 2 && c == '<'))
2022b66bab38SBram Moolenaar 	{
2023b66bab38SBram Moolenaar 	    if (putc(Ctrl_V, fd) < 0)
2024b66bab38SBram Moolenaar 		return FAIL;
2025b66bab38SBram Moolenaar 	}
2026b66bab38SBram Moolenaar 	if (putc(c, fd) < 0)
2027b66bab38SBram Moolenaar 	    return FAIL;
2028b66bab38SBram Moolenaar     }
2029b66bab38SBram Moolenaar     return OK;
2030b66bab38SBram Moolenaar }
2031b66bab38SBram Moolenaar 
2032b66bab38SBram Moolenaar /*
2033b66bab38SBram Moolenaar  * Check all mappings for the presence of special key codes.
2034b66bab38SBram Moolenaar  * Used after ":set term=xxx".
2035b66bab38SBram Moolenaar  */
2036b66bab38SBram Moolenaar     void
check_map_keycodes(void)2037b66bab38SBram Moolenaar check_map_keycodes(void)
2038b66bab38SBram Moolenaar {
2039b66bab38SBram Moolenaar     mapblock_T	*mp;
2040b66bab38SBram Moolenaar     char_u	*p;
2041b66bab38SBram Moolenaar     int		i;
2042b66bab38SBram Moolenaar     char_u	buf[3];
2043b66bab38SBram Moolenaar     int		abbr;
2044b66bab38SBram Moolenaar     int		hash;
2045b66bab38SBram Moolenaar     buf_T	*bp;
2046e31ee868SBram Moolenaar     ESTACK_CHECK_DECLARATION
2047b66bab38SBram Moolenaar 
2048b66bab38SBram Moolenaar     validate_maphash();
20491a47ae32SBram Moolenaar     // avoids giving error messages
20501a47ae32SBram Moolenaar     estack_push(ETYPE_INTERNAL, (char_u *)"mappings", 0);
2051e31ee868SBram Moolenaar     ESTACK_CHECK_SETUP
2052b66bab38SBram Moolenaar 
205332aa1020SBram Moolenaar     // Do this once for each buffer, and then once for global
2054b66bab38SBram Moolenaar     // mappings/abbreviations with bp == NULL
2055b66bab38SBram Moolenaar     for (bp = firstbuf; ; bp = bp->b_next)
2056b66bab38SBram Moolenaar     {
2057b66bab38SBram Moolenaar 	// Do the loop twice: Once for mappings, once for abbreviations.
2058b66bab38SBram Moolenaar 	// Then loop over all map hash lists.
2059b66bab38SBram Moolenaar 	for (abbr = 0; abbr <= 1; ++abbr)
2060b66bab38SBram Moolenaar 	    for (hash = 0; hash < 256; ++hash)
2061b66bab38SBram Moolenaar 	    {
2062b66bab38SBram Moolenaar 		if (abbr)
2063b66bab38SBram Moolenaar 		{
2064b66bab38SBram Moolenaar 		    if (hash)	    // there is only one abbr list
2065b66bab38SBram Moolenaar 			break;
2066b66bab38SBram Moolenaar 		    if (bp != NULL)
2067b66bab38SBram Moolenaar 			mp = bp->b_first_abbr;
2068b66bab38SBram Moolenaar 		    else
2069b66bab38SBram Moolenaar 			mp = first_abbr;
2070b66bab38SBram Moolenaar 		}
2071b66bab38SBram Moolenaar 		else
2072b66bab38SBram Moolenaar 		{
2073b66bab38SBram Moolenaar 		    if (bp != NULL)
2074b66bab38SBram Moolenaar 			mp = bp->b_maphash[hash];
2075b66bab38SBram Moolenaar 		    else
2076b66bab38SBram Moolenaar 			mp = maphash[hash];
2077b66bab38SBram Moolenaar 		}
2078b66bab38SBram Moolenaar 		for ( ; mp != NULL; mp = mp->m_next)
2079b66bab38SBram Moolenaar 		{
2080b66bab38SBram Moolenaar 		    for (i = 0; i <= 1; ++i)	// do this twice
2081b66bab38SBram Moolenaar 		    {
2082b66bab38SBram Moolenaar 			if (i == 0)
2083b66bab38SBram Moolenaar 			    p = mp->m_keys;	// once for the "from" part
2084b66bab38SBram Moolenaar 			else
2085b66bab38SBram Moolenaar 			    p = mp->m_str;	// and once for the "to" part
2086b66bab38SBram Moolenaar 			while (*p)
2087b66bab38SBram Moolenaar 			{
2088b66bab38SBram Moolenaar 			    if (*p == K_SPECIAL)
2089b66bab38SBram Moolenaar 			    {
2090b66bab38SBram Moolenaar 				++p;
2091b66bab38SBram Moolenaar 				if (*p < 128)   // for "normal" tcap entries
2092b66bab38SBram Moolenaar 				{
2093b66bab38SBram Moolenaar 				    buf[0] = p[0];
2094b66bab38SBram Moolenaar 				    buf[1] = p[1];
2095b66bab38SBram Moolenaar 				    buf[2] = NUL;
2096b66bab38SBram Moolenaar 				    (void)add_termcap_entry(buf, FALSE);
2097b66bab38SBram Moolenaar 				}
2098b66bab38SBram Moolenaar 				++p;
2099b66bab38SBram Moolenaar 			    }
2100b66bab38SBram Moolenaar 			    ++p;
2101b66bab38SBram Moolenaar 			}
2102b66bab38SBram Moolenaar 		    }
2103b66bab38SBram Moolenaar 		}
2104b66bab38SBram Moolenaar 	    }
2105b66bab38SBram Moolenaar 	if (bp == NULL)
2106b66bab38SBram Moolenaar 	    break;
2107b66bab38SBram Moolenaar     }
2108e31ee868SBram Moolenaar     ESTACK_CHECK_NOW
21091a47ae32SBram Moolenaar     estack_pop();
2110b66bab38SBram Moolenaar }
2111b66bab38SBram Moolenaar 
2112b66bab38SBram Moolenaar #if defined(FEAT_EVAL) || defined(PROTO)
2113b66bab38SBram Moolenaar /*
2114b66bab38SBram Moolenaar  * Check the string "keys" against the lhs of all mappings.
2115b66bab38SBram Moolenaar  * Return pointer to rhs of mapping (mapblock->m_str).
2116b66bab38SBram Moolenaar  * NULL when no mapping found.
2117b66bab38SBram Moolenaar  */
2118b66bab38SBram Moolenaar     char_u *
check_map(char_u * keys,int mode,int exact,int ign_mod,int abbr,mapblock_T ** mp_ptr,int * local_ptr)2119b66bab38SBram Moolenaar check_map(
2120b66bab38SBram Moolenaar     char_u	*keys,
2121b66bab38SBram Moolenaar     int		mode,
2122b66bab38SBram Moolenaar     int		exact,		// require exact match
2123b66bab38SBram Moolenaar     int		ign_mod,	// ignore preceding modifier
2124b66bab38SBram Moolenaar     int		abbr,		// do abbreviations
2125b66bab38SBram Moolenaar     mapblock_T	**mp_ptr,	// return: pointer to mapblock or NULL
2126b66bab38SBram Moolenaar     int		*local_ptr)	// return: buffer-local mapping or NULL
2127b66bab38SBram Moolenaar {
2128b66bab38SBram Moolenaar     int		hash;
2129b66bab38SBram Moolenaar     int		len, minlen;
2130b66bab38SBram Moolenaar     mapblock_T	*mp;
2131b66bab38SBram Moolenaar     char_u	*s;
2132b66bab38SBram Moolenaar     int		local;
2133b66bab38SBram Moolenaar 
2134b66bab38SBram Moolenaar     validate_maphash();
2135b66bab38SBram Moolenaar 
2136b66bab38SBram Moolenaar     len = (int)STRLEN(keys);
2137b66bab38SBram Moolenaar     for (local = 1; local >= 0; --local)
2138b66bab38SBram Moolenaar 	// loop over all hash lists
2139b66bab38SBram Moolenaar 	for (hash = 0; hash < 256; ++hash)
2140b66bab38SBram Moolenaar 	{
2141b66bab38SBram Moolenaar 	    if (abbr)
2142b66bab38SBram Moolenaar 	    {
2143b66bab38SBram Moolenaar 		if (hash > 0)		// there is only one list.
2144b66bab38SBram Moolenaar 		    break;
2145b66bab38SBram Moolenaar 		if (local)
2146b66bab38SBram Moolenaar 		    mp = curbuf->b_first_abbr;
2147b66bab38SBram Moolenaar 		else
2148b66bab38SBram Moolenaar 		    mp = first_abbr;
2149b66bab38SBram Moolenaar 	    }
2150b66bab38SBram Moolenaar 	    else if (local)
2151b66bab38SBram Moolenaar 		mp = curbuf->b_maphash[hash];
2152b66bab38SBram Moolenaar 	    else
2153b66bab38SBram Moolenaar 		mp = maphash[hash];
2154b66bab38SBram Moolenaar 	    for ( ; mp != NULL; mp = mp->m_next)
2155b66bab38SBram Moolenaar 	    {
2156b66bab38SBram Moolenaar 		// skip entries with wrong mode, wrong length and not matching
2157b66bab38SBram Moolenaar 		// ones
2158b66bab38SBram Moolenaar 		if ((mp->m_mode & mode) && (!exact || mp->m_keylen == len))
2159b66bab38SBram Moolenaar 		{
2160b66bab38SBram Moolenaar 		    if (len > mp->m_keylen)
2161b66bab38SBram Moolenaar 			minlen = mp->m_keylen;
2162b66bab38SBram Moolenaar 		    else
2163b66bab38SBram Moolenaar 			minlen = len;
2164b66bab38SBram Moolenaar 		    s = mp->m_keys;
2165b66bab38SBram Moolenaar 		    if (ign_mod && s[0] == K_SPECIAL && s[1] == KS_MODIFIER
2166b66bab38SBram Moolenaar 							       && s[2] != NUL)
2167b66bab38SBram Moolenaar 		    {
2168b66bab38SBram Moolenaar 			s += 3;
2169b66bab38SBram Moolenaar 			if (len > mp->m_keylen - 3)
2170b66bab38SBram Moolenaar 			    minlen = mp->m_keylen - 3;
2171b66bab38SBram Moolenaar 		    }
2172b66bab38SBram Moolenaar 		    if (STRNCMP(s, keys, minlen) == 0)
2173b66bab38SBram Moolenaar 		    {
2174b66bab38SBram Moolenaar 			if (mp_ptr != NULL)
2175b66bab38SBram Moolenaar 			    *mp_ptr = mp;
2176b66bab38SBram Moolenaar 			if (local_ptr != NULL)
2177b66bab38SBram Moolenaar 			    *local_ptr = local;
2178b66bab38SBram Moolenaar 			return mp->m_str;
2179b66bab38SBram Moolenaar 		    }
2180b66bab38SBram Moolenaar 		}
2181b66bab38SBram Moolenaar 	    }
2182b66bab38SBram Moolenaar 	}
2183b66bab38SBram Moolenaar 
2184b66bab38SBram Moolenaar     return NULL;
2185b66bab38SBram Moolenaar }
2186b66bab38SBram Moolenaar 
2187*4a15504eSYegappan Lakshmanan     static void
get_maparg(typval_T * argvars,typval_T * rettv,int exact)2188b66bab38SBram Moolenaar get_maparg(typval_T *argvars, typval_T *rettv, int exact)
2189b66bab38SBram Moolenaar {
2190b66bab38SBram Moolenaar     char_u	*keys;
21919c65253fSBram Moolenaar     char_u	*keys_simplified;
2192b66bab38SBram Moolenaar     char_u	*which;
2193b66bab38SBram Moolenaar     char_u	buf[NUMBUFLEN];
2194b66bab38SBram Moolenaar     char_u	*keys_buf = NULL;
21959c65253fSBram Moolenaar     char_u	*alt_keys_buf = NULL;
21969c65253fSBram Moolenaar     int		did_simplify = FALSE;
2197b66bab38SBram Moolenaar     char_u	*rhs;
2198b66bab38SBram Moolenaar     int		mode;
2199b66bab38SBram Moolenaar     int		abbr = FALSE;
2200b66bab38SBram Moolenaar     int		get_dict = FALSE;
2201b66bab38SBram Moolenaar     mapblock_T	*mp;
2202a55ba06fSBram Moolenaar     mapblock_T	*mp_simplified = NULL;
2203b66bab38SBram Moolenaar     int		buffer_local;
22049c65253fSBram Moolenaar     int		flags = REPTERM_FROM_PART | REPTERM_DO_LT;
2205b66bab38SBram Moolenaar 
2206b66bab38SBram Moolenaar     // return empty string for failure
2207b66bab38SBram Moolenaar     rettv->v_type = VAR_STRING;
2208b66bab38SBram Moolenaar     rettv->vval.v_string = NULL;
2209b66bab38SBram Moolenaar 
2210b66bab38SBram Moolenaar     keys = tv_get_string(&argvars[0]);
2211b66bab38SBram Moolenaar     if (*keys == NUL)
2212b66bab38SBram Moolenaar 	return;
2213b66bab38SBram Moolenaar 
2214b66bab38SBram Moolenaar     if (argvars[1].v_type != VAR_UNKNOWN)
2215b66bab38SBram Moolenaar     {
2216b66bab38SBram Moolenaar 	which = tv_get_string_buf_chk(&argvars[1], buf);
2217b66bab38SBram Moolenaar 	if (argvars[2].v_type != VAR_UNKNOWN)
2218b66bab38SBram Moolenaar 	{
221904d594b9SBram Moolenaar 	    abbr = (int)tv_get_bool(&argvars[2]);
2220b66bab38SBram Moolenaar 	    if (argvars[3].v_type != VAR_UNKNOWN)
222104d594b9SBram Moolenaar 		get_dict = (int)tv_get_bool(&argvars[3]);
2222b66bab38SBram Moolenaar 	}
2223b66bab38SBram Moolenaar     }
2224b66bab38SBram Moolenaar     else
2225b66bab38SBram Moolenaar 	which = (char_u *)"";
2226b66bab38SBram Moolenaar     if (which == NULL)
2227b66bab38SBram Moolenaar 	return;
2228b66bab38SBram Moolenaar 
2229b66bab38SBram Moolenaar     mode = get_map_mode(&which, 0);
2230b66bab38SBram Moolenaar 
22319c65253fSBram Moolenaar     keys_simplified = replace_termcodes(keys, &keys_buf, flags, &did_simplify);
22329c65253fSBram Moolenaar     rhs = check_map(keys_simplified, mode, exact, FALSE, abbr,
22339c65253fSBram Moolenaar 							   &mp, &buffer_local);
22349c65253fSBram Moolenaar     if (did_simplify)
22359c65253fSBram Moolenaar     {
22369c65253fSBram Moolenaar 	// When the lhs is being simplified the not-simplified keys are
22378e7d6223SBram Moolenaar 	// preferred for printing, like in do_map().
22389c65253fSBram Moolenaar 	// The "rhs" and "buffer_local" values are not expected to change.
22399c65253fSBram Moolenaar 	mp_simplified = mp;
22409c65253fSBram Moolenaar 	(void)replace_termcodes(keys, &alt_keys_buf,
22419c65253fSBram Moolenaar 					flags | REPTERM_NO_SIMPLIFY, NULL);
22429c65253fSBram Moolenaar 	rhs = check_map(alt_keys_buf, mode, exact, FALSE, abbr, &mp,
22439c65253fSBram Moolenaar 								&buffer_local);
22449c65253fSBram Moolenaar     }
2245b66bab38SBram Moolenaar 
2246b66bab38SBram Moolenaar     if (!get_dict)
2247b66bab38SBram Moolenaar     {
2248b66bab38SBram Moolenaar 	// Return a string.
2249b66bab38SBram Moolenaar 	if (rhs != NULL)
2250b66bab38SBram Moolenaar 	{
2251b66bab38SBram Moolenaar 	    if (*rhs == NUL)
2252b66bab38SBram Moolenaar 		rettv->vval.v_string = vim_strsave((char_u *)"<Nop>");
2253b66bab38SBram Moolenaar 	    else
2254b66bab38SBram Moolenaar 		rettv->vval.v_string = str2special_save(rhs, FALSE);
2255b66bab38SBram Moolenaar 	}
2256b66bab38SBram Moolenaar 
2257b66bab38SBram Moolenaar     }
2258b66bab38SBram Moolenaar     else if (rettv_dict_alloc(rettv) != FAIL && rhs != NULL)
2259b66bab38SBram Moolenaar     {
2260b66bab38SBram Moolenaar 	// Return a dictionary.
2261b66bab38SBram Moolenaar 	char_u	    *lhs = str2special_save(mp->m_keys, TRUE);
2262b66bab38SBram Moolenaar 	char_u	    *mapmode = map_mode_to_chars(mp->m_mode);
2263b66bab38SBram Moolenaar 	dict_T	    *dict = rettv->vval.v_dict;
2264b66bab38SBram Moolenaar 
2265b66bab38SBram Moolenaar 	dict_add_string(dict, "lhs", lhs);
22669c65253fSBram Moolenaar 	vim_free(lhs);
22679c65253fSBram Moolenaar 	dict_add_string(dict, "lhsraw", mp->m_keys);
22689c65253fSBram Moolenaar 	if (did_simplify)
22699c65253fSBram Moolenaar 	    // Also add the value for the simplified entry.
22709c65253fSBram Moolenaar 	    dict_add_string(dict, "lhsrawalt", mp_simplified->m_keys);
2271b66bab38SBram Moolenaar 	dict_add_string(dict, "rhs", mp->m_orig_str);
2272b66bab38SBram Moolenaar 	dict_add_number(dict, "noremap", mp->m_noremap ? 1L : 0L);
22732da0f0c4SBram Moolenaar 	dict_add_number(dict, "script", mp->m_noremap == REMAP_SCRIPT
22742da0f0c4SBram Moolenaar 								    ? 1L : 0L);
2275b66bab38SBram Moolenaar 	dict_add_number(dict, "expr", mp->m_expr ? 1L : 0L);
2276b66bab38SBram Moolenaar 	dict_add_number(dict, "silent", mp->m_silent ? 1L : 0L);
2277b66bab38SBram Moolenaar 	dict_add_number(dict, "sid", (long)mp->m_script_ctx.sc_sid);
2278b66bab38SBram Moolenaar 	dict_add_number(dict, "lnum", (long)mp->m_script_ctx.sc_lnum);
2279b66bab38SBram Moolenaar 	dict_add_number(dict, "buffer", (long)buffer_local);
2280b66bab38SBram Moolenaar 	dict_add_number(dict, "nowait", mp->m_nowait ? 1L : 0L);
2281b66bab38SBram Moolenaar 	dict_add_string(dict, "mode", mapmode);
2282b66bab38SBram Moolenaar 
2283b66bab38SBram Moolenaar 	vim_free(mapmode);
2284b66bab38SBram Moolenaar     }
22859c65253fSBram Moolenaar 
22869c65253fSBram Moolenaar     vim_free(keys_buf);
22879c65253fSBram Moolenaar     vim_free(alt_keys_buf);
2288b66bab38SBram Moolenaar }
22894c9243f9SBram Moolenaar 
22904c9243f9SBram Moolenaar /*
2291*4a15504eSYegappan Lakshmanan  * "maparg()" function
2292*4a15504eSYegappan Lakshmanan  */
2293*4a15504eSYegappan Lakshmanan     void
f_maparg(typval_T * argvars,typval_T * rettv)2294*4a15504eSYegappan Lakshmanan f_maparg(typval_T *argvars, typval_T *rettv)
2295*4a15504eSYegappan Lakshmanan {
2296*4a15504eSYegappan Lakshmanan     if (in_vim9script()
2297*4a15504eSYegappan Lakshmanan 	    && (check_for_string_arg(argvars, 0) == FAIL
2298*4a15504eSYegappan Lakshmanan 		|| check_for_opt_string_arg(argvars, 1) == FAIL
2299*4a15504eSYegappan Lakshmanan 		|| (argvars[1].v_type != VAR_UNKNOWN
2300*4a15504eSYegappan Lakshmanan 		    && (check_for_opt_bool_arg(argvars, 2) == FAIL
2301*4a15504eSYegappan Lakshmanan 			|| (argvars[2].v_type != VAR_UNKNOWN
2302*4a15504eSYegappan Lakshmanan 			    && check_for_opt_bool_arg(argvars, 3) == FAIL)))))
2303*4a15504eSYegappan Lakshmanan 		return;
2304*4a15504eSYegappan Lakshmanan 
2305*4a15504eSYegappan Lakshmanan     get_maparg(argvars, rettv, TRUE);
2306*4a15504eSYegappan Lakshmanan }
2307*4a15504eSYegappan Lakshmanan 
2308*4a15504eSYegappan Lakshmanan /*
2309*4a15504eSYegappan Lakshmanan  * "mapcheck()" function
2310*4a15504eSYegappan Lakshmanan  */
2311*4a15504eSYegappan Lakshmanan     void
f_mapcheck(typval_T * argvars,typval_T * rettv)2312*4a15504eSYegappan Lakshmanan f_mapcheck(typval_T *argvars, typval_T *rettv)
2313*4a15504eSYegappan Lakshmanan {
2314*4a15504eSYegappan Lakshmanan     if (in_vim9script()
2315*4a15504eSYegappan Lakshmanan 	    && (check_for_string_arg(argvars, 0) == FAIL
2316*4a15504eSYegappan Lakshmanan 		|| check_for_opt_string_arg(argvars, 1) == FAIL
2317*4a15504eSYegappan Lakshmanan 		|| (argvars[1].v_type != VAR_UNKNOWN
2318*4a15504eSYegappan Lakshmanan 		    && check_for_opt_bool_arg(argvars, 2) == FAIL)))
2319*4a15504eSYegappan Lakshmanan 	return;
2320*4a15504eSYegappan Lakshmanan 
2321*4a15504eSYegappan Lakshmanan     get_maparg(argvars, rettv, FALSE);
2322*4a15504eSYegappan Lakshmanan }
2323*4a15504eSYegappan Lakshmanan 
2324*4a15504eSYegappan Lakshmanan /*
23254c9243f9SBram Moolenaar  * "mapset()" function
23264c9243f9SBram Moolenaar  */
23274c9243f9SBram Moolenaar     void
f_mapset(typval_T * argvars,typval_T * rettv UNUSED)23284c9243f9SBram Moolenaar f_mapset(typval_T *argvars, typval_T *rettv UNUSED)
23294c9243f9SBram Moolenaar {
23304c9243f9SBram Moolenaar     char_u	*keys_buf = NULL;
23314c9243f9SBram Moolenaar     char_u	*which;
23324c9243f9SBram Moolenaar     int		mode;
23334c9243f9SBram Moolenaar     char_u	buf[NUMBUFLEN];
23344c9243f9SBram Moolenaar     int		is_abbr;
23354c9243f9SBram Moolenaar     dict_T	*d;
23364c9243f9SBram Moolenaar     char_u	*lhs;
23379c65253fSBram Moolenaar     char_u	*lhsraw;
23389c65253fSBram Moolenaar     char_u	*lhsrawalt;
23394c9243f9SBram Moolenaar     char_u	*rhs;
2340c94c1467SBram Moolenaar     char_u	*orig_rhs;
2341c94c1467SBram Moolenaar     char_u	*arg_buf = NULL;
23424c9243f9SBram Moolenaar     int		noremap;
23434c9243f9SBram Moolenaar     int		expr;
23444c9243f9SBram Moolenaar     int		silent;
23457ba1e4d3SBram Moolenaar     int		buffer;
23464c9243f9SBram Moolenaar     scid_T	sid;
23474c9243f9SBram Moolenaar     linenr_T	lnum;
23484c9243f9SBram Moolenaar     mapblock_T	**map_table = maphash;
23494c9243f9SBram Moolenaar     mapblock_T  **abbr_table = &first_abbr;
23504c9243f9SBram Moolenaar     int		nowait;
23514c9243f9SBram Moolenaar     char_u	*arg;
23524c9243f9SBram Moolenaar 
235383494b4aSYegappan Lakshmanan     if (in_vim9script()
235483494b4aSYegappan Lakshmanan 	    && (check_for_string_arg(argvars, 0) == FAIL
235583494b4aSYegappan Lakshmanan 		|| check_for_bool_arg(argvars, 1) == FAIL
235683494b4aSYegappan Lakshmanan 		|| check_for_dict_arg(argvars, 2) == FAIL))
235783494b4aSYegappan Lakshmanan 	return;
235883494b4aSYegappan Lakshmanan 
23594c9243f9SBram Moolenaar     which = tv_get_string_buf_chk(&argvars[0], buf);
23601b912980SBram Moolenaar     if (which == NULL)
23611b912980SBram Moolenaar 	return;
23624c9243f9SBram Moolenaar     mode = get_map_mode(&which, 0);
236374273e66SBram Moolenaar     is_abbr = (int)tv_get_bool(&argvars[1]);
23644c9243f9SBram Moolenaar 
23654c9243f9SBram Moolenaar     if (argvars[2].v_type != VAR_DICT)
23664c9243f9SBram Moolenaar     {
23674c9243f9SBram Moolenaar 	emsg(_(e_dictkey));
23684c9243f9SBram Moolenaar 	return;
23694c9243f9SBram Moolenaar     }
23704c9243f9SBram Moolenaar     d = argvars[2].vval.v_dict;
23714c9243f9SBram Moolenaar 
23724c9243f9SBram Moolenaar     // Get the values in the same order as above in get_maparg().
23734c9243f9SBram Moolenaar     lhs = dict_get_string(d, (char_u *)"lhs", FALSE);
23749c65253fSBram Moolenaar     lhsraw = dict_get_string(d, (char_u *)"lhsraw", FALSE);
23759c65253fSBram Moolenaar     lhsrawalt = dict_get_string(d, (char_u *)"lhsrawalt", FALSE);
23764c9243f9SBram Moolenaar     rhs = dict_get_string(d, (char_u *)"rhs", FALSE);
23779c65253fSBram Moolenaar     if (lhs == NULL || lhsraw == NULL || rhs == NULL)
23784c9243f9SBram Moolenaar     {
23799c65253fSBram Moolenaar 	emsg(_("E460: entries missing in mapset() dict argument"));
23804c9243f9SBram Moolenaar 	return;
23814c9243f9SBram Moolenaar     }
2382c94c1467SBram Moolenaar     orig_rhs = rhs;
2383c94c1467SBram Moolenaar     rhs = replace_termcodes(rhs, &arg_buf,
2384c94c1467SBram Moolenaar 					REPTERM_DO_LT | REPTERM_SPECIAL, NULL);
23854c9243f9SBram Moolenaar 
23864c9243f9SBram Moolenaar     noremap = dict_get_number(d, (char_u *)"noremap") ? REMAP_NONE: 0;
23874c9243f9SBram Moolenaar     if (dict_get_number(d, (char_u *)"script") != 0)
23884c9243f9SBram Moolenaar 	noremap = REMAP_SCRIPT;
23894c9243f9SBram Moolenaar     expr = dict_get_number(d, (char_u *)"expr") != 0;
23904c9243f9SBram Moolenaar     silent = dict_get_number(d, (char_u *)"silent") != 0;
23914c9243f9SBram Moolenaar     sid = dict_get_number(d, (char_u *)"sid");
23924c9243f9SBram Moolenaar     lnum = dict_get_number(d, (char_u *)"lnum");
23937ba1e4d3SBram Moolenaar     buffer = dict_get_number(d, (char_u *)"buffer");
23947ba1e4d3SBram Moolenaar     nowait = dict_get_number(d, (char_u *)"nowait") != 0;
23957ba1e4d3SBram Moolenaar     // mode from the dict is not used
23967ba1e4d3SBram Moolenaar 
23977ba1e4d3SBram Moolenaar     if (buffer)
23984c9243f9SBram Moolenaar     {
23994c9243f9SBram Moolenaar 	map_table = curbuf->b_maphash;
24004c9243f9SBram Moolenaar 	abbr_table = &curbuf->b_first_abbr;
24014c9243f9SBram Moolenaar     }
24024c9243f9SBram Moolenaar 
24034c9243f9SBram Moolenaar     // Delete any existing mapping for this lhs and mode.
24047ba1e4d3SBram Moolenaar     if (buffer)
24057ba1e4d3SBram Moolenaar     {
24067ba1e4d3SBram Moolenaar 	arg = alloc(STRLEN(lhs) + STRLEN("<buffer>") + 1);
24077ba1e4d3SBram Moolenaar 	if (arg == NULL)
24087ba1e4d3SBram Moolenaar 	    return;
24097ba1e4d3SBram Moolenaar 	STRCPY(arg, "<buffer>");
24107ba1e4d3SBram Moolenaar 	STRCPY(arg + 8, lhs);
24117ba1e4d3SBram Moolenaar     }
24127ba1e4d3SBram Moolenaar     else
24137ba1e4d3SBram Moolenaar     {
24144c9243f9SBram Moolenaar 	arg = vim_strsave(lhs);
24154c9243f9SBram Moolenaar 	if (arg == NULL)
24164c9243f9SBram Moolenaar 	    return;
24177ba1e4d3SBram Moolenaar     }
24184c9243f9SBram Moolenaar     do_map(1, arg, mode, is_abbr);
24194c9243f9SBram Moolenaar     vim_free(arg);
24204c9243f9SBram Moolenaar 
24219c65253fSBram Moolenaar     (void)map_add(map_table, abbr_table, lhsraw, rhs, orig_rhs, noremap,
24229c65253fSBram Moolenaar 	    nowait, silent, mode, is_abbr, expr, sid, lnum, 0);
24239c65253fSBram Moolenaar     if (lhsrawalt != NULL)
24249c65253fSBram Moolenaar 	(void)map_add(map_table, abbr_table, lhsrawalt, rhs, orig_rhs, noremap,
24259c65253fSBram Moolenaar 		nowait, silent, mode, is_abbr, expr, sid, lnum, 1);
24264c9243f9SBram Moolenaar     vim_free(keys_buf);
2427c94c1467SBram Moolenaar     vim_free(arg_buf);
24284c9243f9SBram Moolenaar }
2429b66bab38SBram Moolenaar #endif
2430b66bab38SBram Moolenaar 
24314c9243f9SBram Moolenaar 
2432b66bab38SBram Moolenaar #if defined(MSWIN) || defined(MACOS_X)
2433b66bab38SBram Moolenaar 
2434b66bab38SBram Moolenaar # define VIS_SEL	(VISUAL+SELECTMODE)	// abbreviation
2435b66bab38SBram Moolenaar 
2436b66bab38SBram Moolenaar /*
2437b66bab38SBram Moolenaar  * Default mappings for some often used keys.
2438b66bab38SBram Moolenaar  */
2439b66bab38SBram Moolenaar struct initmap
2440b66bab38SBram Moolenaar {
2441b66bab38SBram Moolenaar     char_u	*arg;
2442b66bab38SBram Moolenaar     int		mode;
2443b66bab38SBram Moolenaar };
2444b66bab38SBram Moolenaar 
2445b66bab38SBram Moolenaar # ifdef FEAT_GUI_MSWIN
2446b66bab38SBram Moolenaar // Use the Windows (CUA) keybindings. (GUI)
2447b66bab38SBram Moolenaar static struct initmap initmappings[] =
2448b66bab38SBram Moolenaar {
2449b66bab38SBram Moolenaar 	// paste, copy and cut
2450b66bab38SBram Moolenaar 	{(char_u *)"<S-Insert> \"*P", NORMAL},
2451b66bab38SBram Moolenaar 	{(char_u *)"<S-Insert> \"-d\"*P", VIS_SEL},
2452b66bab38SBram Moolenaar 	{(char_u *)"<S-Insert> <C-R><C-O>*", INSERT+CMDLINE},
2453b66bab38SBram Moolenaar 	{(char_u *)"<C-Insert> \"*y", VIS_SEL},
2454b66bab38SBram Moolenaar 	{(char_u *)"<S-Del> \"*d", VIS_SEL},
2455b66bab38SBram Moolenaar 	{(char_u *)"<C-Del> \"*d", VIS_SEL},
2456b66bab38SBram Moolenaar 	{(char_u *)"<C-X> \"*d", VIS_SEL},
2457b66bab38SBram Moolenaar 	// Missing: CTRL-C (cancel) and CTRL-V (block selection)
2458b66bab38SBram Moolenaar };
2459b66bab38SBram Moolenaar # endif
2460b66bab38SBram Moolenaar 
2461b66bab38SBram Moolenaar # if defined(MSWIN) && (!defined(FEAT_GUI) || defined(VIMDLL))
2462b66bab38SBram Moolenaar // Use the Windows (CUA) keybindings. (Console)
2463b66bab38SBram Moolenaar static struct initmap cinitmappings[] =
2464b66bab38SBram Moolenaar {
2465b66bab38SBram Moolenaar 	{(char_u *)"\316w <C-Home>", NORMAL+VIS_SEL},
2466b66bab38SBram Moolenaar 	{(char_u *)"\316w <C-Home>", INSERT+CMDLINE},
2467b66bab38SBram Moolenaar 	{(char_u *)"\316u <C-End>", NORMAL+VIS_SEL},
2468b66bab38SBram Moolenaar 	{(char_u *)"\316u <C-End>", INSERT+CMDLINE},
2469b66bab38SBram Moolenaar 
2470b66bab38SBram Moolenaar 	// paste, copy and cut
2471b66bab38SBram Moolenaar #  ifdef FEAT_CLIPBOARD
2472b66bab38SBram Moolenaar 	{(char_u *)"\316\324 \"*P", NORMAL},	    // SHIFT-Insert is "*P
2473b66bab38SBram Moolenaar 	{(char_u *)"\316\324 \"-d\"*P", VIS_SEL},   // SHIFT-Insert is "-d"*P
2474b66bab38SBram Moolenaar 	{(char_u *)"\316\324 \022\017*", INSERT},  // SHIFT-Insert is ^R^O*
2475b66bab38SBram Moolenaar 	{(char_u *)"\316\325 \"*y", VIS_SEL},	    // CTRL-Insert is "*y
2476b66bab38SBram Moolenaar 	{(char_u *)"\316\327 \"*d", VIS_SEL},	    // SHIFT-Del is "*d
2477b66bab38SBram Moolenaar 	{(char_u *)"\316\330 \"*d", VIS_SEL},	    // CTRL-Del is "*d
2478b66bab38SBram Moolenaar 	{(char_u *)"\030 \"*d", VIS_SEL},	    // CTRL-X is "*d
2479b66bab38SBram Moolenaar #  else
2480b66bab38SBram Moolenaar 	{(char_u *)"\316\324 P", NORMAL},	    // SHIFT-Insert is P
2481b66bab38SBram Moolenaar 	{(char_u *)"\316\324 \"-dP", VIS_SEL},	    // SHIFT-Insert is "-dP
2482b66bab38SBram Moolenaar 	{(char_u *)"\316\324 \022\017\"", INSERT}, // SHIFT-Insert is ^R^O"
2483b66bab38SBram Moolenaar 	{(char_u *)"\316\325 y", VIS_SEL},	    // CTRL-Insert is y
2484b66bab38SBram Moolenaar 	{(char_u *)"\316\327 d", VIS_SEL},	    // SHIFT-Del is d
2485b66bab38SBram Moolenaar 	{(char_u *)"\316\330 d", VIS_SEL},	    // CTRL-Del is d
2486b66bab38SBram Moolenaar #  endif
2487b66bab38SBram Moolenaar };
2488b66bab38SBram Moolenaar # endif
2489b66bab38SBram Moolenaar 
2490b66bab38SBram Moolenaar # if defined(MACOS_X)
2491b66bab38SBram Moolenaar static struct initmap initmappings[] =
2492b66bab38SBram Moolenaar {
2493b66bab38SBram Moolenaar 	// Use the Standard MacOS binding.
2494b66bab38SBram Moolenaar 	// paste, copy and cut
2495b66bab38SBram Moolenaar 	{(char_u *)"<D-v> \"*P", NORMAL},
2496b66bab38SBram Moolenaar 	{(char_u *)"<D-v> \"-d\"*P", VIS_SEL},
2497b66bab38SBram Moolenaar 	{(char_u *)"<D-v> <C-R>*", INSERT+CMDLINE},
2498b66bab38SBram Moolenaar 	{(char_u *)"<D-c> \"*y", VIS_SEL},
2499b66bab38SBram Moolenaar 	{(char_u *)"<D-x> \"*d", VIS_SEL},
2500b66bab38SBram Moolenaar 	{(char_u *)"<Backspace> \"-d", VIS_SEL},
2501b66bab38SBram Moolenaar };
2502b66bab38SBram Moolenaar # endif
2503b66bab38SBram Moolenaar 
2504b66bab38SBram Moolenaar # undef VIS_SEL
2505b66bab38SBram Moolenaar #endif
2506b66bab38SBram Moolenaar 
2507b66bab38SBram Moolenaar /*
2508b66bab38SBram Moolenaar  * Set up default mappings.
2509b66bab38SBram Moolenaar  */
2510b66bab38SBram Moolenaar     void
init_mappings(void)2511b66bab38SBram Moolenaar init_mappings(void)
2512b66bab38SBram Moolenaar {
2513b66bab38SBram Moolenaar #if defined(MSWIN) || defined(MACOS_X)
2514b66bab38SBram Moolenaar     int		i;
2515b66bab38SBram Moolenaar 
2516b66bab38SBram Moolenaar # if defined(MSWIN) && (!defined(FEAT_GUI_MSWIN) || defined(VIMDLL))
2517b66bab38SBram Moolenaar #  ifdef VIMDLL
2518b66bab38SBram Moolenaar     if (!gui.starting)
2519b66bab38SBram Moolenaar #  endif
2520b66bab38SBram Moolenaar     {
2521eeec2548SK.Takata 	for (i = 0; i < (int)ARRAY_LENGTH(cinitmappings); ++i)
2522b66bab38SBram Moolenaar 	    add_map(cinitmappings[i].arg, cinitmappings[i].mode);
2523b66bab38SBram Moolenaar     }
2524b66bab38SBram Moolenaar # endif
2525b66bab38SBram Moolenaar # if defined(FEAT_GUI_MSWIN) || defined(MACOS_X)
2526eeec2548SK.Takata     for (i = 0; i < (int)ARRAY_LENGTH(initmappings); ++i)
2527b66bab38SBram Moolenaar 	add_map(initmappings[i].arg, initmappings[i].mode);
2528b66bab38SBram Moolenaar # endif
2529b66bab38SBram Moolenaar #endif
2530b66bab38SBram Moolenaar }
2531b66bab38SBram Moolenaar 
2532b66bab38SBram Moolenaar #if defined(MSWIN) || defined(FEAT_CMDWIN) || defined(MACOS_X) \
2533b66bab38SBram Moolenaar 							     || defined(PROTO)
2534b66bab38SBram Moolenaar /*
2535b66bab38SBram Moolenaar  * Add a mapping "map" for mode "mode".
2536b66bab38SBram Moolenaar  * Need to put string in allocated memory, because do_map() will modify it.
2537b66bab38SBram Moolenaar  */
2538b66bab38SBram Moolenaar     void
add_map(char_u * map,int mode)2539b66bab38SBram Moolenaar add_map(char_u *map, int mode)
2540b66bab38SBram Moolenaar {
2541b66bab38SBram Moolenaar     char_u	*s;
2542b66bab38SBram Moolenaar     char_u	*cpo_save = p_cpo;
2543b66bab38SBram Moolenaar 
2544e5a2dc87SBram Moolenaar     p_cpo = empty_option;	// Allow <> notation
2545b66bab38SBram Moolenaar     s = vim_strsave(map);
2546b66bab38SBram Moolenaar     if (s != NULL)
2547b66bab38SBram Moolenaar     {
2548b66bab38SBram Moolenaar 	(void)do_map(0, s, mode, FALSE);
2549b66bab38SBram Moolenaar 	vim_free(s);
2550b66bab38SBram Moolenaar     }
2551b66bab38SBram Moolenaar     p_cpo = cpo_save;
2552b66bab38SBram Moolenaar }
2553b66bab38SBram Moolenaar #endif
2554b66bab38SBram Moolenaar 
2555e677df8dSBram Moolenaar #if defined(FEAT_LANGMAP) || defined(PROTO)
2556e677df8dSBram Moolenaar /*
2557e677df8dSBram Moolenaar  * Any character has an equivalent 'langmap' character.  This is used for
2558e677df8dSBram Moolenaar  * keyboards that have a special language mode that sends characters above
2559e677df8dSBram Moolenaar  * 128 (although other characters can be translated too).  The "to" field is a
2560e677df8dSBram Moolenaar  * Vim command character.  This avoids having to switch the keyboard back to
2561e677df8dSBram Moolenaar  * ASCII mode when leaving Insert mode.
2562e677df8dSBram Moolenaar  *
2563e677df8dSBram Moolenaar  * langmap_mapchar[] maps any of 256 chars to an ASCII char used for Vim
2564e677df8dSBram Moolenaar  * commands.
2565e677df8dSBram Moolenaar  * langmap_mapga.ga_data is a sorted table of langmap_entry_T.  This does the
2566e677df8dSBram Moolenaar  * same as langmap_mapchar[] for characters >= 256.
2567e677df8dSBram Moolenaar  *
2568e677df8dSBram Moolenaar  * Use growarray for 'langmap' chars >= 256
2569e677df8dSBram Moolenaar  */
2570e677df8dSBram Moolenaar typedef struct
2571e677df8dSBram Moolenaar {
2572e677df8dSBram Moolenaar     int	    from;
2573e677df8dSBram Moolenaar     int     to;
2574e677df8dSBram Moolenaar } langmap_entry_T;
2575e677df8dSBram Moolenaar 
2576e677df8dSBram Moolenaar static garray_T langmap_mapga;
2577e677df8dSBram Moolenaar 
2578e677df8dSBram Moolenaar /*
2579e677df8dSBram Moolenaar  * Search for an entry in "langmap_mapga" for "from".  If found set the "to"
2580e677df8dSBram Moolenaar  * field.  If not found insert a new entry at the appropriate location.
2581e677df8dSBram Moolenaar  */
2582e677df8dSBram Moolenaar     static void
langmap_set_entry(int from,int to)2583e677df8dSBram Moolenaar langmap_set_entry(int from, int to)
2584e677df8dSBram Moolenaar {
2585e677df8dSBram Moolenaar     langmap_entry_T *entries = (langmap_entry_T *)(langmap_mapga.ga_data);
2586e677df8dSBram Moolenaar     int		    a = 0;
2587e677df8dSBram Moolenaar     int		    b = langmap_mapga.ga_len;
2588e677df8dSBram Moolenaar 
2589e677df8dSBram Moolenaar     // Do a binary search for an existing entry.
2590e677df8dSBram Moolenaar     while (a != b)
2591e677df8dSBram Moolenaar     {
2592e677df8dSBram Moolenaar 	int i = (a + b) / 2;
2593e677df8dSBram Moolenaar 	int d = entries[i].from - from;
2594e677df8dSBram Moolenaar 
2595e677df8dSBram Moolenaar 	if (d == 0)
2596e677df8dSBram Moolenaar 	{
2597e677df8dSBram Moolenaar 	    entries[i].to = to;
2598e677df8dSBram Moolenaar 	    return;
2599e677df8dSBram Moolenaar 	}
2600e677df8dSBram Moolenaar 	if (d < 0)
2601e677df8dSBram Moolenaar 	    a = i + 1;
2602e677df8dSBram Moolenaar 	else
2603e677df8dSBram Moolenaar 	    b = i;
2604e677df8dSBram Moolenaar     }
2605e677df8dSBram Moolenaar 
2606e677df8dSBram Moolenaar     if (ga_grow(&langmap_mapga, 1) != OK)
2607e677df8dSBram Moolenaar 	return;  // out of memory
2608e677df8dSBram Moolenaar 
2609e677df8dSBram Moolenaar     // insert new entry at position "a"
2610e677df8dSBram Moolenaar     entries = (langmap_entry_T *)(langmap_mapga.ga_data) + a;
2611e677df8dSBram Moolenaar     mch_memmove(entries + 1, entries,
2612e677df8dSBram Moolenaar 			(langmap_mapga.ga_len - a) * sizeof(langmap_entry_T));
2613e677df8dSBram Moolenaar     ++langmap_mapga.ga_len;
2614e677df8dSBram Moolenaar     entries[0].from = from;
2615e677df8dSBram Moolenaar     entries[0].to = to;
2616e677df8dSBram Moolenaar }
2617e677df8dSBram Moolenaar 
2618e677df8dSBram Moolenaar /*
2619e677df8dSBram Moolenaar  * Apply 'langmap' to multi-byte character "c" and return the result.
2620e677df8dSBram Moolenaar  */
2621e677df8dSBram Moolenaar     int
langmap_adjust_mb(int c)2622e677df8dSBram Moolenaar langmap_adjust_mb(int c)
2623e677df8dSBram Moolenaar {
2624e677df8dSBram Moolenaar     langmap_entry_T *entries = (langmap_entry_T *)(langmap_mapga.ga_data);
2625e677df8dSBram Moolenaar     int a = 0;
2626e677df8dSBram Moolenaar     int b = langmap_mapga.ga_len;
2627e677df8dSBram Moolenaar 
2628e677df8dSBram Moolenaar     while (a != b)
2629e677df8dSBram Moolenaar     {
2630e677df8dSBram Moolenaar 	int i = (a + b) / 2;
2631e677df8dSBram Moolenaar 	int d = entries[i].from - c;
2632e677df8dSBram Moolenaar 
2633e677df8dSBram Moolenaar 	if (d == 0)
2634e677df8dSBram Moolenaar 	    return entries[i].to;  // found matching entry
2635e677df8dSBram Moolenaar 	if (d < 0)
2636e677df8dSBram Moolenaar 	    a = i + 1;
2637e677df8dSBram Moolenaar 	else
2638e677df8dSBram Moolenaar 	    b = i;
2639e677df8dSBram Moolenaar     }
2640e677df8dSBram Moolenaar     return c;  // no entry found, return "c" unmodified
2641e677df8dSBram Moolenaar }
2642e677df8dSBram Moolenaar 
2643e677df8dSBram Moolenaar     void
langmap_init(void)2644e677df8dSBram Moolenaar langmap_init(void)
2645e677df8dSBram Moolenaar {
2646e677df8dSBram Moolenaar     int i;
2647e677df8dSBram Moolenaar 
2648e677df8dSBram Moolenaar     for (i = 0; i < 256; i++)
2649e677df8dSBram Moolenaar 	langmap_mapchar[i] = i;	 // we init with a one-to-one map
2650e677df8dSBram Moolenaar     ga_init2(&langmap_mapga, sizeof(langmap_entry_T), 8);
2651e677df8dSBram Moolenaar }
2652e677df8dSBram Moolenaar 
2653e677df8dSBram Moolenaar /*
2654e677df8dSBram Moolenaar  * Called when langmap option is set; the language map can be
2655e677df8dSBram Moolenaar  * changed at any time!
2656e677df8dSBram Moolenaar  */
2657e677df8dSBram Moolenaar     void
langmap_set(void)2658e677df8dSBram Moolenaar langmap_set(void)
2659e677df8dSBram Moolenaar {
2660e677df8dSBram Moolenaar     char_u  *p;
2661e677df8dSBram Moolenaar     char_u  *p2;
2662e677df8dSBram Moolenaar     int	    from, to;
2663e677df8dSBram Moolenaar 
2664e677df8dSBram Moolenaar     ga_clear(&langmap_mapga);		    // clear the previous map first
2665e677df8dSBram Moolenaar     langmap_init();			    // back to one-to-one map
2666e677df8dSBram Moolenaar 
2667e677df8dSBram Moolenaar     for (p = p_langmap; p[0] != NUL; )
2668e677df8dSBram Moolenaar     {
2669e677df8dSBram Moolenaar 	for (p2 = p; p2[0] != NUL && p2[0] != ',' && p2[0] != ';';
2670e677df8dSBram Moolenaar 							       MB_PTR_ADV(p2))
2671e677df8dSBram Moolenaar 	{
2672e677df8dSBram Moolenaar 	    if (p2[0] == '\\' && p2[1] != NUL)
2673e677df8dSBram Moolenaar 		++p2;
2674e677df8dSBram Moolenaar 	}
2675e677df8dSBram Moolenaar 	if (p2[0] == ';')
2676e677df8dSBram Moolenaar 	    ++p2;	    // abcd;ABCD form, p2 points to A
2677e677df8dSBram Moolenaar 	else
2678e677df8dSBram Moolenaar 	    p2 = NULL;	    // aAbBcCdD form, p2 is NULL
2679e677df8dSBram Moolenaar 	while (p[0])
2680e677df8dSBram Moolenaar 	{
2681e677df8dSBram Moolenaar 	    if (p[0] == ',')
2682e677df8dSBram Moolenaar 	    {
2683e677df8dSBram Moolenaar 		++p;
2684e677df8dSBram Moolenaar 		break;
2685e677df8dSBram Moolenaar 	    }
2686e677df8dSBram Moolenaar 	    if (p[0] == '\\' && p[1] != NUL)
2687e677df8dSBram Moolenaar 		++p;
2688e677df8dSBram Moolenaar 	    from = (*mb_ptr2char)(p);
2689e677df8dSBram Moolenaar 	    to = NUL;
2690e677df8dSBram Moolenaar 	    if (p2 == NULL)
2691e677df8dSBram Moolenaar 	    {
2692e677df8dSBram Moolenaar 		MB_PTR_ADV(p);
2693e677df8dSBram Moolenaar 		if (p[0] != ',')
2694e677df8dSBram Moolenaar 		{
2695e677df8dSBram Moolenaar 		    if (p[0] == '\\')
2696e677df8dSBram Moolenaar 			++p;
2697e677df8dSBram Moolenaar 		    to = (*mb_ptr2char)(p);
2698e677df8dSBram Moolenaar 		}
2699e677df8dSBram Moolenaar 	    }
2700e677df8dSBram Moolenaar 	    else
2701e677df8dSBram Moolenaar 	    {
2702e677df8dSBram Moolenaar 		if (p2[0] != ',')
2703e677df8dSBram Moolenaar 		{
2704e677df8dSBram Moolenaar 		    if (p2[0] == '\\')
2705e677df8dSBram Moolenaar 			++p2;
2706e677df8dSBram Moolenaar 		    to = (*mb_ptr2char)(p2);
2707e677df8dSBram Moolenaar 		}
2708e677df8dSBram Moolenaar 	    }
2709e677df8dSBram Moolenaar 	    if (to == NUL)
2710e677df8dSBram Moolenaar 	    {
2711e677df8dSBram Moolenaar 		semsg(_("E357: 'langmap': Matching character missing for %s"),
2712e677df8dSBram Moolenaar 							     transchar(from));
2713e677df8dSBram Moolenaar 		return;
2714e677df8dSBram Moolenaar 	    }
2715e677df8dSBram Moolenaar 
2716e677df8dSBram Moolenaar 	    if (from >= 256)
2717e677df8dSBram Moolenaar 		langmap_set_entry(from, to);
2718e677df8dSBram Moolenaar 	    else
2719e677df8dSBram Moolenaar 		langmap_mapchar[from & 255] = to;
2720e677df8dSBram Moolenaar 
2721e677df8dSBram Moolenaar 	    // Advance to next pair
2722e677df8dSBram Moolenaar 	    MB_PTR_ADV(p);
2723e677df8dSBram Moolenaar 	    if (p2 != NULL)
2724e677df8dSBram Moolenaar 	    {
2725e677df8dSBram Moolenaar 		MB_PTR_ADV(p2);
2726e677df8dSBram Moolenaar 		if (*p == ';')
2727e677df8dSBram Moolenaar 		{
2728e677df8dSBram Moolenaar 		    p = p2;
2729e677df8dSBram Moolenaar 		    if (p[0] != NUL)
2730e677df8dSBram Moolenaar 		    {
2731e677df8dSBram Moolenaar 			if (p[0] != ',')
2732e677df8dSBram Moolenaar 			{
2733e677df8dSBram Moolenaar 			    semsg(_("E358: 'langmap': Extra characters after semicolon: %s"), p);
2734e677df8dSBram Moolenaar 			    return;
2735e677df8dSBram Moolenaar 			}
2736e677df8dSBram Moolenaar 			++p;
2737e677df8dSBram Moolenaar 		    }
2738e677df8dSBram Moolenaar 		    break;
2739e677df8dSBram Moolenaar 		}
2740e677df8dSBram Moolenaar 	    }
2741e677df8dSBram Moolenaar 	}
2742e677df8dSBram Moolenaar     }
2743e677df8dSBram Moolenaar }
2744e677df8dSBram Moolenaar #endif
2745e677df8dSBram Moolenaar 
2746b66bab38SBram Moolenaar     static void
do_exmap(exarg_T * eap,int isabbrev)2747b66bab38SBram Moolenaar do_exmap(exarg_T *eap, int isabbrev)
2748b66bab38SBram Moolenaar {
2749b66bab38SBram Moolenaar     int	    mode;
2750b66bab38SBram Moolenaar     char_u  *cmdp;
2751b66bab38SBram Moolenaar 
2752b66bab38SBram Moolenaar     cmdp = eap->cmd;
2753b66bab38SBram Moolenaar     mode = get_map_mode(&cmdp, eap->forceit || isabbrev);
2754b66bab38SBram Moolenaar 
2755b66bab38SBram Moolenaar     switch (do_map((*cmdp == 'n') ? 2 : (*cmdp == 'u'),
2756b66bab38SBram Moolenaar 						    eap->arg, mode, isabbrev))
2757b66bab38SBram Moolenaar     {
2758b66bab38SBram Moolenaar 	case 1: emsg(_(e_invarg));
2759b66bab38SBram Moolenaar 		break;
2760e29a27f6SBram Moolenaar 	case 2: emsg((isabbrev ? _(e_no_such_abbreviation)
2761e29a27f6SBram Moolenaar 						      : _(e_no_such_mapping)));
2762b66bab38SBram Moolenaar 		break;
2763b66bab38SBram Moolenaar     }
2764b66bab38SBram Moolenaar }
2765b66bab38SBram Moolenaar 
2766b66bab38SBram Moolenaar /*
2767b66bab38SBram Moolenaar  * ":abbreviate" and friends.
2768b66bab38SBram Moolenaar  */
2769b66bab38SBram Moolenaar     void
ex_abbreviate(exarg_T * eap)2770b66bab38SBram Moolenaar ex_abbreviate(exarg_T *eap)
2771b66bab38SBram Moolenaar {
2772b66bab38SBram Moolenaar     do_exmap(eap, TRUE);	// almost the same as mapping
2773b66bab38SBram Moolenaar }
2774b66bab38SBram Moolenaar 
2775b66bab38SBram Moolenaar /*
2776b66bab38SBram Moolenaar  * ":map" and friends.
2777b66bab38SBram Moolenaar  */
2778b66bab38SBram Moolenaar     void
ex_map(exarg_T * eap)2779b66bab38SBram Moolenaar ex_map(exarg_T *eap)
2780b66bab38SBram Moolenaar {
2781b66bab38SBram Moolenaar     // If we are sourcing .exrc or .vimrc in current directory we
2782b66bab38SBram Moolenaar     // print the mappings for security reasons.
2783b66bab38SBram Moolenaar     if (secure)
2784b66bab38SBram Moolenaar     {
2785b66bab38SBram Moolenaar 	secure = 2;
2786b66bab38SBram Moolenaar 	msg_outtrans(eap->cmd);
2787b66bab38SBram Moolenaar 	msg_putchar('\n');
2788b66bab38SBram Moolenaar     }
2789b66bab38SBram Moolenaar     do_exmap(eap, FALSE);
2790b66bab38SBram Moolenaar }
2791b66bab38SBram Moolenaar 
2792b66bab38SBram Moolenaar /*
2793b66bab38SBram Moolenaar  * ":unmap" and friends.
2794b66bab38SBram Moolenaar  */
2795b66bab38SBram Moolenaar     void
ex_unmap(exarg_T * eap)2796b66bab38SBram Moolenaar ex_unmap(exarg_T *eap)
2797b66bab38SBram Moolenaar {
2798b66bab38SBram Moolenaar     do_exmap(eap, FALSE);
2799b66bab38SBram Moolenaar }
2800b66bab38SBram Moolenaar 
2801b66bab38SBram Moolenaar /*
2802b66bab38SBram Moolenaar  * ":mapclear" and friends.
2803b66bab38SBram Moolenaar  */
2804b66bab38SBram Moolenaar     void
ex_mapclear(exarg_T * eap)2805b66bab38SBram Moolenaar ex_mapclear(exarg_T *eap)
2806b66bab38SBram Moolenaar {
2807b66bab38SBram Moolenaar     map_clear(eap->cmd, eap->arg, eap->forceit, FALSE);
2808b66bab38SBram Moolenaar }
2809b66bab38SBram Moolenaar 
2810b66bab38SBram Moolenaar /*
2811b66bab38SBram Moolenaar  * ":abclear" and friends.
2812b66bab38SBram Moolenaar  */
2813b66bab38SBram Moolenaar     void
ex_abclear(exarg_T * eap)2814b66bab38SBram Moolenaar ex_abclear(exarg_T *eap)
2815b66bab38SBram Moolenaar {
2816b66bab38SBram Moolenaar     map_clear(eap->cmd, eap->arg, TRUE, TRUE);
2817b66bab38SBram Moolenaar }
2818