www.pudn.com > vim53src.zip > misc2.c
/* vi:set ts=8 sts=4 sw=4: * * VIM - Vi IMproved by Bram Moolenaar * * Do ":help uganda" in Vim to read copying and usage conditions. * Do ":help credits" in Vim to see a list of people who contributed. */ /* * misc2.c: Various functions. */ #include "vim.h" #ifdef HAVE_FCNTL_H # include/* for chdir() */ #endif /* * coladvance(col) * * Try to advance the Cursor to the specified column. * * return OK if desired column is reached, FAIL if not */ int coladvance(wcol) colnr_t wcol; { int idx; char_u *ptr; colnr_t col; ptr = ml_get_curline(); /* try to advance to the specified column */ idx = -1; col = 0; while (col <= wcol && *ptr) { ++idx; /* Count a tab for what it's worth (if list mode not on) */ col += lbr_chartabsize(ptr, col); ++ptr; } /* * In Insert mode it is allowed to be one char beyond the end of the line. * Also in Visual mode, when 'selection' is not "old". */ if (((State & INSERT) || (VIsual_active && *p_sel != 'o')) && col <= wcol) ++idx; if (idx < 0) curwin->w_cursor.col = 0; else curwin->w_cursor.col = idx; #ifdef MULTI_BYTE /* prevent cursor from moving on the trail byte */ if (is_dbcs) AdjustCursorForMultiByteCharacter(); #endif if (col <= wcol) return FAIL; /* Couldn't reach column */ else return OK; /* Reached column */ } /* * inc(p) * * Increment the line pointer 'p' crossing line boundaries as necessary. * Return 1 when crossing a line, -1 when at end of file, 0 otherwise. */ int inc_cursor() { return inc(&curwin->w_cursor); } int inc(lp) FPOS *lp; { char_u *p = ml_get_pos(lp); if (*p != NUL) /* still within line, move to next char (may be NUL) */ { #ifdef MULTI_BYTE if (is_dbcs && IsLeadByte(p[0]) && p[1] != NUL) { lp->col += 2; return ((p[2] != NUL) ? 0 : 1); } #endif lp->col++; return ((p[1] != NUL) ? 0 : 1); } if (lp->lnum != curbuf->b_ml.ml_line_count) /* there is a next line */ { lp->col = 0; lp->lnum++; return 1; } return -1; } /* * incl(lp): same as inc(), but skip the NUL at the end of non-empty lines */ int incl(lp) FPOS *lp; { int r; if ((r = inc(lp)) == 1 && lp->col) r = inc(lp); return r; } /* * dec(p) * * Decrement the line pointer 'p' crossing line boundaries as necessary. * Return 1 when crossing a line, -1 when at start of file, 0 otherwise. */ int dec_cursor() { #ifdef MULTI_BYTE return (is_dbcs ? han_dec(&curwin->w_cursor) : dec(&curwin->w_cursor)); #else return dec(&curwin->w_cursor); #endif } int dec(lp) FPOS *lp; { if (lp->col > 0) { /* still within line */ lp->col--; return 0; } if (lp->lnum > 1) { /* there is a prior line */ lp->lnum--; lp->col = STRLEN(ml_get(lp->lnum)); return 1; } return -1; /* at start of file */ } /* * decl(lp): same as dec(), but skip the NUL at the end of non-empty lines */ int decl(lp) FPOS *lp; { int r; if ((r = dec(lp)) == 1 && lp->col) r = dec(lp); return r; } /* * Make sure curwin->w_cursor.lnum is valid. */ void check_cursor_lnum() { if (curwin->w_cursor.lnum > curbuf->b_ml.ml_line_count) curwin->w_cursor.lnum = curbuf->b_ml.ml_line_count; if (curwin->w_cursor.lnum <= 0) curwin->w_cursor.lnum = 1; } /* * Make sure curwin->w_cursor.col is valid. */ void check_cursor_col() { colnr_t len; len = STRLEN(ml_get_curline()); if (len == 0) curwin->w_cursor.col = 0; else if (curwin->w_cursor.col >= len) { if (State & INSERT || restart_edit) curwin->w_cursor.col = len; else curwin->w_cursor.col = len - 1; } } /* * make sure curwin->w_cursor in on a valid character */ void adjust_cursor() { check_cursor_lnum(); check_cursor_col(); } /* * Make sure curwin->w_cursor is not on the NUL at the end of the line. * Allow it when in Visual mode and 'selection' is not "old". */ void adjust_cursor_col() { if ((!VIsual_active || *p_sel == 'o') && curwin->w_cursor.col && gchar_cursor() == NUL) --curwin->w_cursor.col; } /* * When curwin->w_leftcol has changed, adjust the cursor position. * Return TRUE if the cursor was moved. */ int leftcol_changed() { long lastcol; colnr_t s, e; int retval = FALSE; changed_cline_bef_curs(); lastcol = curwin->w_leftcol + Columns - (curwin->w_p_nu ? 8 : 0) - 1; validate_virtcol(); /* * If the cursor is right or left of the screen, move it to last or first * character. */ if (curwin->w_virtcol > (colnr_t)lastcol) { retval = TRUE; coladvance((colnr_t)lastcol); } else if (curwin->w_virtcol < curwin->w_leftcol) { retval = TRUE; (void)coladvance(curwin->w_leftcol); } /* * If the start of the character under the cursor is not on the screen, * advance the cursor one more char. If this fails (last char of the * line) adjust the scrolling. */ getvcol(curwin, &curwin->w_cursor, &s, NULL, &e); if (e > (colnr_t)lastcol) { retval = TRUE; coladvance(s - 1); } else if (s < curwin->w_leftcol) { retval = TRUE; if (coladvance(e + 1) == FAIL) /* there isn't another character */ { curwin->w_leftcol = s; /* adjust w_leftcol instead */ changed_cline_bef_curs(); } } redraw_later(NOT_VALID); return retval; } /********************************************************************** * Various routines dealing with allocation and deallocation of memory. */ #if defined(MEM_PROFILE) || defined(PROTO) # define MEM_SIZES 8200 static long_u mem_allocs[MEM_SIZES]; static long_u mem_frees[MEM_SIZES]; static long_u mem_allocated; static long_u mem_freed; static long_u mem_peak; static long_u num_alloc; static long_u num_freed; static void mem_pre_alloc_s __ARGS((size_t *sizep)); static void mem_pre_alloc_l __ARGS((long_u *sizep)); static void mem_post_alloc __ARGS((void **pp, size_t size)); static void mem_pre_free __ARGS((void **pp)); static void mem_pre_alloc_s(sizep) size_t *sizep; { *sizep += sizeof(size_t); } static void mem_pre_alloc_l(sizep) long_u *sizep; { *sizep += sizeof(size_t); } static void mem_post_alloc(pp, size) void **pp; size_t size; { if (*pp == NULL) return; size -= sizeof(size_t); *(long_u *)*pp = size; if (size <= MEM_SIZES-1) mem_allocs[size-1]++; else mem_allocs[MEM_SIZES-1]++; mem_allocated += size; if (mem_allocated - mem_freed > mem_peak) mem_peak = mem_allocated - mem_freed; num_alloc++; *pp = (void *)((char *)*pp + sizeof(size_t)); } static void mem_pre_free(pp) void **pp; { long_u size; *pp = (void *)((char *)*pp - sizeof(size_t)); size = *(size_t *)*pp; if (size <= MEM_SIZES-1) mem_frees[size-1]++; else mem_frees[MEM_SIZES-1]++; mem_freed += size; num_freed++; } /* * called on exit via atexit() */ void vim_mem_profile_dump() { int i, j; printf("\r\n"); j = 0; for (i = 0; i < MEM_SIZES - 1; i++) { if (mem_allocs[i] || mem_frees[i]) { if (mem_frees[i] > mem_allocs[i]) printf("\r\nERROR: "); printf("[%4d / %4lu-%-4lu] ", i + 1, mem_allocs[i], mem_frees[i]); j++; if (j > 3) { j = 0; printf("\r\n"); } } } i = MEM_SIZES - 1; if (mem_allocs[i]) { printf("\r\n"); if (mem_frees[i] > mem_allocs[i]) printf("ERROR: "); printf("[>%d / %4lu-%-4lu]", i, mem_allocs[i], mem_frees[i]); } printf("\r\n\n[bytes] total alloc-freed %lu-%lu, in use %lu, peak use %lu\r\n", mem_allocated, mem_freed, mem_allocated - mem_freed, mem_peak); printf("[calls] total re/malloc()'s %lu, total free()'s %lu\r\n\n", num_alloc, num_freed); } #endif /* MEM_PROFILE */ /* * Some memory is reserved for error messages and for being able to * call mf_release_all(), which needs some memory for mf_trans_add(). */ #define KEEP_ROOM 8192L static void vim_strup __ARGS((char_u *p)); /* * Note: if unsinged is 16 bits we can only allocate up to 64K with alloc(). * Use lalloc for larger blocks. */ char_u * alloc(size) unsigned size; { return (lalloc((long_u)size, TRUE)); } /* * Allocate memory and set all bytes to zero. */ char_u * alloc_clear(size) unsigned size; { char_u *p; p = (lalloc((long_u)size, TRUE)); if (p != NULL) (void)vim_memset(p, 0, (size_t)size); return p; } /* * alloc() with check for maximum line length */ char_u * alloc_check(size) unsigned size; { #if !defined(UNIX) && !defined(__EMX__) if (sizeof(int) == 2 && size > 0x7fff) { EMSG("Line is becoming too long"); return NULL; } #endif return (lalloc((long_u)size, TRUE)); } /* * Allocate memory like lalloc() and set all bytes to zero. */ char_u * lalloc_clear(size, message) long_u size; int message; { char_u *p; p = (lalloc(size, message)); if (p != NULL) (void)vim_memset(p, 0, (size_t)size); return p; } char_u * lalloc(size, message) long_u size; int message; { char_u *p; /* pointer to new storage space */ static int releasing = FALSE; /* don't do mf_release_all() recursive */ int try_again; if (size <= 0) { EMSGN("Internal error: lalloc(%ld, )", size); return NULL; } #ifdef MEM_PROFILE mem_pre_alloc_l(&size); #endif #if defined(MSDOS) && !defined(DJGPP) if (size >= 0xfff0) /* in MSDOS we can't deal with >64K blocks */ p = NULL; else #endif /* * If out of memory, try to release some memfile blocks. * If some blocks are released call malloc again. */ for (;;) { if ((p = (char_u *)malloc((size_t)size)) != NULL) { if (mch_avail_mem(TRUE) < KEEP_ROOM && !releasing) { /* System is low... no go! */ vim_free((char *)p); p = NULL; } } /* * Remember that mf_release_all() is being called to avoid an endless loop, * because mf_release_all() may call alloc() recursively. */ if (p != NULL || releasing) break; releasing = TRUE; try_again = mf_release_all(); releasing = FALSE; if (!try_again) break; } if (message && p == NULL) do_outofmem_msg(); #ifdef MEM_PROFILE mem_post_alloc((void **)&p, (size_t)size); #endif return (p); } #if defined(MEM_PROFILE) || defined(PROTO) /* * realloc(), with memory profiling. */ void * vim_realloc(ptr, size) void *ptr; size_t size; { void *p; mem_pre_free(&ptr); mem_pre_alloc_s(&size); p = realloc(ptr, size); mem_post_alloc(&p, size); return p; } #endif /* * Avoid repeating the error message many times (they take 1 second each). * Did_outofmem_msg is reset when a character is read. */ void do_outofmem_msg() { if (!did_outofmem_msg) { emsg(e_outofmem); did_outofmem_msg = TRUE; } } /* * copy a string into newly allocated memory */ char_u * vim_strsave(string) char_u *string; { char_u *p; unsigned len; len = STRLEN(string) + 1; p = alloc(len); if (p != NULL) mch_memmove(p, string, (size_t)len); return p; } char_u * vim_strnsave(string, len) char_u *string; int len; { char_u *p; p = alloc((unsigned)(len + 1)); if (p != NULL) { STRNCPY(p, string, len); p[len] = NUL; } return p; } /* * like vim_strnsave(), but remove backslashes from the string. */ char_u * vim_strnsave_esc(string, len) char_u *string; int len; { char_u *p1, *p2; p1 = alloc((unsigned) (len + 1)); if (p1 != NULL) { STRNCPY(p1, string, len); p1[len] = NUL; for (p2 = p1; *p2; ++p2) if (*p2 == '\\' && *(p2 + 1) != NUL) STRCPY(p2, p2 + 1); } return p1; } /* * Same as vim_strsave(), but any characters found in esc_chars are preceded * by a backslash. */ char_u * vim_strsave_escaped(string, esc_chars) char_u *string; char_u *esc_chars; { char_u *p; char_u *p2; char_u *escaped_string; unsigned length; /* * First count the number of backslashes required. * Then allocate the memory and insert them. */ length = 1; /* count the trailing '/' and NUL */ for (p = string; *p; p++) { if (vim_strchr(esc_chars, *p) != NULL) ++length; /* count a backslash */ ++length; /* count an ordinary char */ } escaped_string = alloc(length); if (escaped_string != NULL) { p2 = escaped_string; for (p = string; *p; p++) { if (vim_strchr(esc_chars, *p) != NULL) *p2++ = '\\'; *p2++ = *p; } *p2 = NUL; } return escaped_string; } /* * like vim_strsave(), but make all characters uppercase. */ char_u * vim_strsave_up(string) char_u *string; { char_u *p1; p1 = vim_strsave(string); vim_strup(p1); return p1; } /* * like vim_strnsave(), but make all characters uppercase. */ char_u * vim_strnsave_up(string, len) char_u *string; int len; { char_u *p1; p1 = vim_strnsave(string, len); vim_strup(p1); return p1; } static void vim_strup(p) char_u *p; { char_u *p2; int c; if (p != NULL) { p2 = p; while ((c = *p2) != NUL) *p2++ = TO_UPPER(c); } } /* * copy a number of spaces */ void copy_spaces(ptr, count) char_u *ptr; size_t count; { size_t i = count; char_u *p = ptr; while (i--) *p++ = ' '; } /* * delete spaces at the end of a string */ void del_trailing_spaces(ptr) char_u *ptr; { char_u *q; q = ptr + STRLEN(ptr); while (--q > ptr && vim_iswhite(q[0]) && q[-1] != '\\' && q[-1] != Ctrl('V')) *q = NUL; } /* * vim_strncpy() * * This is here because strncpy() does not guarantee successful results when * the to and from strings overlap. It is only currently called from nextwild() * which copies part of the command line to another part of the command line. * This produced garbage when expanding files etc in the middle of the command * line (on my terminal, anyway) -- webb. */ void vim_strncpy(to, from, len) char_u *to; char_u *from; int len; { int i; if (to <= from) { while (len-- && *from) *to++ = *from++; if (len >= 0) *to = *from; /* Copy NUL */ } else { for (i = 0; i < len; i++) { to++; if (*from++ == NUL) { i++; break; } } for (; i > 0; i--) *--to = *--from; } } /* * Isolate one part of a string option where parts are separated with commas. * The part is copied into buf[maxlen]. * "*option" is advanced to the next part. * The length is returned. */ int copy_option_part(option, buf, maxlen, sep_chars) char_u **option; char_u *buf; int maxlen; char *sep_chars; { int len = 0; char_u *p = *option; /* skip '.' at start of option part, for 'suffixes' */ if (*p == '.') buf[len++] = *p++; while (*p && vim_strchr((char_u *)sep_chars, *p) == NULL) { /* * Skip backslash before a separator character and space. */ if (p[0] == '\\' && vim_strchr((char_u *)sep_chars, p[1]) != NULL) ++p; if (len < maxlen - 1) buf[len++] = *p; ++p; } buf[len] = NUL; p = skip_to_option_part(p); /* p points to next file name */ *option = p; return len; } /* * replacement for free() that ignores NULL pointers */ void vim_free(x) void *x; { if (x != NULL) { #ifdef MEM_PROFILE mem_pre_free(&x); #endif free(x); } } #ifndef HAVE_MEMSET void * vim_memset(ptr, c, size) void *ptr; int c; size_t size; { char *p = ptr; while (size-- > 0) *p++ = c; return ptr; } #endif #ifdef VIM_MEMCMP /* * Return zero when "b1" and "b2" are the same for "len" bytes. * Return non-zero otherwise. */ int vim_memcmp(b1, b2, len) void *b1; void *b2; size_t len; { char_u *p1 = (char_u *)b1, *p2 = (char_u *)b2; for ( ; len > 0; --len) { if (*p1 != *p2) return 1; ++p1; ++p2; } return 0; } #endif #ifdef VIM_MEMMOVE /* * Version of memmove that handles overlapping source and destination. * For systems that don't have a function that is guaranteed to do that (SYSV). */ void mch_memmove(dst_arg, src_arg, len) void *src_arg, *dst_arg; size_t len; { /* * A void doesn't have a size, we use char pointers. */ char *dst = dst_arg, *src = src_arg; /* overlap, copy backwards */ if (dst > src && dst < src + len) { src +=len; dst +=len; while (len-- > 0) *--dst = *--src; } else /* copy forwards */ while (len-- > 0) *dst++ = *src++; } #endif #if (!defined(HAVE_STRCASECMP) && !defined(HAVE_STRICMP)) || defined(PROTO) /* * Compare two strings, ignoring case. * return 0 for match, 1 for difference */ int vim_stricmp(s1, s2) char *s1; char *s2; { for (;;) { if (TO_LOWER(*s1) != TO_LOWER(*s2)) return 1; /* this character different */ if (*s1 == NUL) break; /* strings match until NUL */ ++s1; ++s2; } return 0; /* strings match */ } #endif #if (!defined(HAVE_STRNCASECMP) && !defined(HAVE_STRNICMP)) || defined(PROTO) /* * Compare two strings, for length "len", ignoring case. * return 0 for match, 1 for difference */ int vim_strnicmp(s1, s2, len) char *s1; char *s2; size_t len; { while (len) { if (TO_LOWER(*s1) != TO_LOWER(*s2)) return 1; /* this character different */ if (*s1 == NUL) break; /* strings match until NUL */ ++s1; ++s2; --len; } return 0; /* strings match */ } #endif /* * Version of strchr() and strrchr() that handle unsigned char strings * with characters above 128 correctly. Also it doesn't return a pointer to * the NUL at the end of the string. */ char_u * vim_strchr(string, n) char_u *string; int n; { char_u *p; int c; p = string; while ((c = *p) != NUL) { if (c == n) return p; ++p; } return NULL; } char_u * vim_strrchr(string, n) char_u *string; int n; { char_u *retval = NULL; while (*string) { if (*string == n) retval = string; ++string; } return retval; } /* * Vim's version of strpbrk(), in case it's missing. * Don't generate a prototype for this, causes problems when it's not used. */ #ifndef PROTO # ifndef HAVE_STRPBRK # ifdef vim_strpbrk # undef vim_strpbrk # endif char_u * vim_strpbrk(s, charset) char_u *s; char_u *charset; { while (*s) { if (vim_strchr(charset, *s) != NULL) return s; ++s; } return NULL; } # endif #endif /* * Vim has its own isspace() function, because on some machines isspace() * can't handle characters above 128. */ int vim_isspace(x) int x; { return ((x >= 9 && x <= 13) || x == ' '); } /************************************************************************ * Functions for hanlding growing arrays. */ /* * Clear an allocated growing array. */ void ga_clear(gap) struct growarray *gap; { vim_free(gap->ga_data); ga_init(gap); } /* * Clear a growing array that contains a list of strings. */ void ga_clear_strings(gap) struct growarray *gap; { int i; for (i = 0; i < gap->ga_len; ++i) vim_free(((char_u **)(gap->ga_data))[i]); ga_clear(gap); } /* * Initialize a growing array. Don't forget to set ga_itemsize and * ga_growsize! Or use ga_init2(). */ void ga_init(gap) struct growarray *gap; { gap->ga_data = NULL; gap->ga_room = 0; gap->ga_len = 0; } void ga_init2(gap, itemsize, growsize) struct growarray *gap; int itemsize; int growsize; { ga_init(gap); gap->ga_itemsize = itemsize; gap->ga_growsize = growsize; } /* * Make room in growing array "ga" for at least "n" items. * Return FAIL for failure, OK otherwise. */ int ga_grow(ga, n) struct growarray *ga; int n; { size_t len; char_u *pp; if (ga->ga_room < n) { if (n < ga->ga_growsize) n = ga->ga_growsize; len = ga->ga_itemsize * (ga->ga_len + n); pp = alloc_clear((unsigned)len); if (pp == NULL) return FAIL; ga->ga_room = n; if (ga->ga_data != NULL) { mch_memmove(pp, ga->ga_data, (size_t)(ga->ga_itemsize * ga->ga_len)); vim_free(ga->ga_data); } ga->ga_data = pp; } return OK; } /************************************************************************ * functions that use lookup tables for various things, generally to do with * special key codes. */ /* * Some useful tables. */ static struct modmasktable { int mod_mask; /* Bit-mask for particular key modifier */ char_u name; /* Single letter name of modifier */ } mod_mask_table[] = { {MOD_MASK_ALT, (char_u)'M'}, {MOD_MASK_CTRL, (char_u)'C'}, {MOD_MASK_SHIFT, (char_u)'S'}, {MOD_MASK_2CLICK, (char_u)'2'}, {MOD_MASK_3CLICK, (char_u)'3'}, {MOD_MASK_4CLICK, (char_u)'4'}, #ifdef macintosh {MOD_MASK_CMD, (char_u)'D'}, #endif {0x0, NUL} }; /* * Shifted key terminal codes and their unshifted equivalent. * Don't add mouse codes here, they are handled seperately! */ static char_u shifted_keys_table[] = { /* shifted unshifted */ '&', '9', '@', '1', /* begin */ '&', '0', '@', '2', /* cancel */ '*', '1', '@', '4', /* command */ '*', '2', '@', '5', /* copy */ '*', '3', '@', '6', /* create */ '*', '4', 'k', 'D', /* delete char */ '*', '5', 'k', 'L', /* delete line */ '*', '7', '@', '7', /* end */ '*', '9', '@', '9', /* exit */ '*', '0', '@', '0', /* find */ '#', '1', '%', '1', /* help */ '#', '2', 'k', 'h', /* home */ '#', '3', 'k', 'I', /* insert */ '#', '4', 'k', 'l', /* left arrow */ '%', 'a', '%', '3', /* message */ '%', 'b', '%', '4', /* move */ '%', 'c', '%', '5', /* next */ '%', 'd', '%', '7', /* options */ '%', 'e', '%', '8', /* previous */ '%', 'f', '%', '9', /* print */ '%', 'g', '%', '0', /* redo */ '%', 'h', '&', '3', /* replace */ '%', 'i', 'k', 'r', /* right arrow */ '%', 'j', '&', '5', /* resume */ '!', '1', '&', '6', /* save */ '!', '2', '&', '7', /* suspend */ '!', '3', '&', '8', /* undo */ KS_EXTRA, (int)KE_S_UP, 'k', 'u', /* up arrow */ KS_EXTRA, (int)KE_S_DOWN, 'k', 'd', /* down arrow */ KS_EXTRA, (int)KE_S_F1, 'k', '1', /* F1 */ KS_EXTRA, (int)KE_S_F2, 'k', '2', KS_EXTRA, (int)KE_S_F3, 'k', '3', KS_EXTRA, (int)KE_S_F4, 'k', '4', KS_EXTRA, (int)KE_S_F5, 'k', '5', KS_EXTRA, (int)KE_S_F6, 'k', '6', KS_EXTRA, (int)KE_S_F7, 'k', '7', KS_EXTRA, (int)KE_S_F8, 'k', '8', KS_EXTRA, (int)KE_S_F9, 'k', '9', KS_EXTRA, (int)KE_S_F10, 'k', ';', /* F10 */ KS_EXTRA, (int)KE_S_F11, 'F', '1', KS_EXTRA, (int)KE_S_F12, 'F', '2', KS_EXTRA, (int)KE_S_F13, 'F', '3', KS_EXTRA, (int)KE_S_F14, 'F', '4', KS_EXTRA, (int)KE_S_F15, 'F', '5', KS_EXTRA, (int)KE_S_F16, 'F', '6', KS_EXTRA, (int)KE_S_F17, 'F', '7', KS_EXTRA, (int)KE_S_F18, 'F', '8', KS_EXTRA, (int)KE_S_F19, 'F', '9', KS_EXTRA, (int)KE_S_F20, 'F', 'A', KS_EXTRA, (int)KE_S_F21, 'F', 'B', KS_EXTRA, (int)KE_S_F22, 'F', 'C', KS_EXTRA, (int)KE_S_F23, 'F', 'D', KS_EXTRA, (int)KE_S_F24, 'F', 'E', KS_EXTRA, (int)KE_S_F25, 'F', 'F', KS_EXTRA, (int)KE_S_F26, 'F', 'G', KS_EXTRA, (int)KE_S_F27, 'F', 'H', KS_EXTRA, (int)KE_S_F28, 'F', 'I', KS_EXTRA, (int)KE_S_F29, 'F', 'J', KS_EXTRA, (int)KE_S_F30, 'F', 'K', KS_EXTRA, (int)KE_S_F31, 'F', 'L', KS_EXTRA, (int)KE_S_F32, 'F', 'M', KS_EXTRA, (int)KE_S_F33, 'F', 'N', KS_EXTRA, (int)KE_S_F34, 'F', 'O', KS_EXTRA, (int)KE_S_F35, 'F', 'P', KS_EXTRA, (int)KE_S_TAB, KS_EXTRA, (int)KE_TAB, /* TAB pseudo code*/ NUL }; static struct key_name_entry { int key; /* Special key code or ascii value */ char_u *name; /* Name of key */ } key_names_table[] = { {' ', (char_u *)"Space"}, {TAB, (char_u *)"Tab"}, {K_TAB, (char_u *)"Tab"}, {NL, (char_u *)"NL"}, {NL, (char_u *)"NewLine"}, /* Alternative name */ {NL, (char_u *)"LineFeed"}, /* Alternative name */ {NL, (char_u *)"LF"}, /* Alternative name */ {CR, (char_u *)"CR"}, {CR, (char_u *)"Return"}, /* Alternative name */ {K_BS, (char_u *)"BS"}, {K_BS, (char_u *)"BackSpace"}, /* Alternative name */ {ESC, (char_u *)"Esc"}, {'|', (char_u *)"Bar"}, {'\\', (char_u *)"Bslash"}, {K_DEL, (char_u *)"Del"}, {K_DEL, (char_u *)"Delete"}, /* Alternative name */ {K_UP, (char_u *)"Up"}, {K_DOWN, (char_u *)"Down"}, {K_LEFT, (char_u *)"Left"}, {K_RIGHT, (char_u *)"Right"}, {K_F1, (char_u *)"F1"}, {K_F2, (char_u *)"F2"}, {K_F3, (char_u *)"F3"}, {K_F4, (char_u *)"F4"}, {K_F5, (char_u *)"F5"}, {K_F6, (char_u *)"F6"}, {K_F7, (char_u *)"F7"}, {K_F8, (char_u *)"F8"}, {K_F9, (char_u *)"F9"}, {K_F10, (char_u *)"F10"}, {K_F11, (char_u *)"F11"}, {K_F12, (char_u *)"F12"}, {K_F13, (char_u *)"F13"}, {K_F14, (char_u *)"F14"}, {K_F15, (char_u *)"F15"}, {K_F16, (char_u *)"F16"}, {K_F17, (char_u *)"F17"}, {K_F18, (char_u *)"F18"}, {K_F19, (char_u *)"F19"}, {K_F20, (char_u *)"F20"}, {K_F21, (char_u *)"F21"}, {K_F22, (char_u *)"F22"}, {K_F23, (char_u *)"F23"}, {K_F24, (char_u *)"F24"}, {K_F25, (char_u *)"F25"}, {K_F26, (char_u *)"F26"}, {K_F27, (char_u *)"F27"}, {K_F28, (char_u *)"F28"}, {K_F29, (char_u *)"F29"}, {K_F30, (char_u *)"F30"}, {K_F31, (char_u *)"F31"}, {K_F32, (char_u *)"F32"}, {K_F33, (char_u *)"F33"}, {K_F34, (char_u *)"F34"}, {K_F35, (char_u *)"F35"}, {K_XF1, (char_u *)"F1"}, {K_XF2, (char_u *)"F2"}, {K_XF3, (char_u *)"F3"}, {K_XF4, (char_u *)"F4"}, {K_HELP, (char_u *)"Help"}, {K_UNDO, (char_u *)"Undo"}, {K_INS, (char_u *)"Insert"}, {K_INS, (char_u *)"Ins"}, /* Alternative name */ {K_HOME, (char_u *)"Home"}, {K_END, (char_u *)"End"}, {K_PAGEUP, (char_u *)"PageUp"}, {K_PAGEDOWN, (char_u *)"PageDown"}, {K_KHOME, (char_u *)"kHome"}, {K_KEND, (char_u *)"kEnd"}, {K_KPAGEUP, (char_u *)"kPageUp"}, {K_KPAGEDOWN, (char_u *)"kPageDown"}, {K_KPLUS, (char_u *)"kPlus"}, {K_KMINUS, (char_u *)"kMinus"}, {K_KDIVIDE, (char_u *)"kDivide"}, {K_KMULTIPLY, (char_u *)"kMultiply"}, {K_KENTER, (char_u *)"kEnter"}, {'<', (char_u *)"lt"}, {K_MOUSE, (char_u *)"Mouse"}, {K_LEFTMOUSE, (char_u *)"LeftMouse"}, {K_LEFTDRAG, (char_u *)"LeftDrag"}, {K_LEFTRELEASE, (char_u *)"LeftRelease"}, {K_MIDDLEMOUSE, (char_u *)"MiddleMouse"}, {K_MIDDLEDRAG, (char_u *)"MiddleDrag"}, {K_MIDDLERELEASE, (char_u *)"MiddleRelease"}, {K_RIGHTMOUSE, (char_u *)"RightMouse"}, {K_RIGHTDRAG, (char_u *)"RightDrag"}, {K_RIGHTRELEASE, (char_u *)"RightRelease"}, {K_ZERO, (char_u *)"Nul"}, {0, NULL} }; #define KEY_NAMES_TABLE_LEN (sizeof(key_names_table) / sizeof(struct key_name_entry)) #ifdef USE_MOUSE static struct mousetable { int pseudo_code; /* Code for pseudo mouse event */ int button; /* Which mouse button is it? */ int is_click; /* Is it a mouse button click event? */ int is_drag; /* Is it a mouse drag event? */ } mouse_table[] = { {(int)KE_LEFTMOUSE, MOUSE_LEFT, TRUE, FALSE}, {(int)KE_LEFTDRAG, MOUSE_LEFT, FALSE, TRUE}, {(int)KE_LEFTRELEASE, MOUSE_LEFT, FALSE, FALSE}, {(int)KE_MIDDLEMOUSE, MOUSE_MIDDLE, TRUE, FALSE}, {(int)KE_MIDDLEDRAG, MOUSE_MIDDLE, FALSE, TRUE}, {(int)KE_MIDDLERELEASE, MOUSE_MIDDLE, FALSE, FALSE}, {(int)KE_RIGHTMOUSE, MOUSE_RIGHT, TRUE, FALSE}, {(int)KE_RIGHTDRAG, MOUSE_RIGHT, FALSE, TRUE}, {(int)KE_RIGHTRELEASE, MOUSE_RIGHT, FALSE, FALSE}, /* DRAG without CLICK */ {(int)KE_IGNORE, MOUSE_RELEASE, FALSE, TRUE}, /* RELEASE without CLICK */ {(int)KE_IGNORE, MOUSE_RELEASE, FALSE, FALSE}, {0, 0, 0, 0}, }; #endif /* USE_MOUSE */ /* * Return the modifier mask bit (MOD_MASK_*) which corresponds to the given * modifier name ('S' for Shift, 'C' for Ctrl etc). */ int name_to_mod_mask(c) int c; { int i; for (i = 0; mod_mask_table[i].mod_mask; i++) if (TO_LOWER(c) == TO_LOWER(mod_mask_table[i].name)) return mod_mask_table[i].mod_mask; return 0x0; } /* * Decide whether the given key code (K_*) is a shifted special * key (by looking at mod_mask). If it is, then return the appropriate shifted * key code, otherwise just return the character as is. */ int check_shifted_spec_key(c) int c; { return simplify_key(c, &mod_mask); } /* * Check if if there is a special key code for "key" that includes the * modifiers specified. */ int simplify_key(key, modifiers) int key; int *modifiers; { int i; int key0; int key1; if (*modifiers & MOD_MASK_SHIFT) { if (key == TAB) /* TAB is a special case */ { *modifiers &= ~MOD_MASK_SHIFT; return K_S_TAB; } key0 = KEY2TERMCAP0(key); key1 = KEY2TERMCAP1(key); for (i = 0; shifted_keys_table[i] != NUL; i += 4) if (key0 == shifted_keys_table[i + 2] && key1 == shifted_keys_table[i + 3]) { *modifiers &= ~MOD_MASK_SHIFT; return TERMCAP2KEY(shifted_keys_table[i], shifted_keys_table[i + 1]); } } return key; } /* * Return a string which contains the name of the given key when the given * modifiers are down. */ char_u * get_special_key_name(c, modifiers) int c; int modifiers; { static char_u string[MAX_KEY_NAME_LEN + 1]; int i, idx; int table_idx; char_u *s; string[0] = '<'; idx = 1; /* * Translate shifted special keys into unshifted keys and set modifier. */ if (IS_SPECIAL(c)) { for (i = 0; shifted_keys_table[i]; i += 4) if ( KEY2TERMCAP0(c) == shifted_keys_table[i] && KEY2TERMCAP1(c) == shifted_keys_table[i + 1]) { modifiers |= MOD_MASK_SHIFT; c = TERMCAP2KEY(shifted_keys_table[i + 2], shifted_keys_table[i + 3]); break; } } /* try to find the key in the special key table */ table_idx = find_special_key_in_table(c); /* * When not a known special key, and not a printable character, try to * extract modifiers. */ if (table_idx < 0 && (!vim_isprintc(c) || (c & 0x7f) == ' ') && (c & 0x80)) { c &= 0x7f; modifiers |= MOD_MASK_ALT; /* try again, to find the un-alted key in the special key table */ table_idx = find_special_key_in_table(c); } if (table_idx < 0 && !vim_isprintc(c) && c < ' ') { c += '@'; modifiers |= MOD_MASK_CTRL; } /* translate the modifier into a string */ for (i = 0; mod_mask_table[i].mod_mask; i++) if (modifiers & mod_mask_table[i].mod_mask) { string[idx++] = mod_mask_table[i].name; string[idx++] = (char_u)'-'; } if (table_idx < 0) /* unknown special key, output t_xx */ { if (IS_SPECIAL(c)) { string[idx++] = 't'; string[idx++] = '_'; string[idx++] = KEY2TERMCAP0(c); string[idx++] = KEY2TERMCAP1(c); } /* Not a special key, only modifiers, output directly */ else { if (vim_isprintc(c)) string[idx++] = c; else { s = transchar(c); while (*s) string[idx++] = *s++; } } } else /* use name of special key */ { STRCPY(string + idx, key_names_table[table_idx].name); idx = STRLEN(string); } string[idx++] = '>'; string[idx] = NUL; return string; } /* * Try translating a <> name at (*srcp)[] to dst[]. * Return the number of characters added to dst[], zero for no match. * If there is a match, srcp is advanced to after the <> name. * dst[] must be big enough to hold the result (up to six characters)! */ int trans_special(srcp, dst, keycode) char_u **srcp; char_u *dst; int keycode; /* prefer key code, e.g. K_DEL instead of DEL */ { int modifiers; int key; int dlen = 0; key = find_special_key(srcp, &modifiers, keycode); if (key == 0) return 0; /* Put the appropriate modifier in a string */ if (modifiers != 0) { dst[dlen++] = K_SPECIAL; dst[dlen++] = KS_MODIFIER; dst[dlen++] = modifiers; } if (IS_SPECIAL(key)) { dst[dlen++] = K_SPECIAL; dst[dlen++] = KEY2TERMCAP0(key); dst[dlen++] = KEY2TERMCAP1(key); } else dst[dlen++] = key; return dlen; } /* * Try translating a <> name at (*srcp)[], return the key and modifiers. * srcp is advanced to after the <> name. * returns 0 if there is no match. */ int find_special_key(srcp, modp, keycode) char_u **srcp; int *modp; int keycode; /* prefer key code, e.g. K_DEL instead of DEL */ { char_u *last_dash; char_u *end_of_name; char_u *src; char_u *bp; int modifiers; int bit; int key; src = *srcp; if (src[0] != '<') return 0; /* Find end of modifier list */ last_dash = src; for (bp = src + 1; *bp == '-' || vim_isIDc(*bp); bp++) { if (*bp == '-') { last_dash = bp; if (bp[1] != NUL && bp[2] == '>') ++bp; /* anything accepted, like */ } if (bp[0] == 't' && bp[1] == '_' && bp[2] && bp[3]) bp += 3; /* skip t_xx, xx may be '-' or '>' */ } if (*bp == '>') /* found matching '>' */ { end_of_name = bp + 1; /* Which modifiers are given? */ modifiers = 0x0; for (bp = src + 1; bp < last_dash; bp++) { if (*bp != '-') { bit = name_to_mod_mask(*bp); if (bit == 0x0) break; /* Illegal modifier name */ modifiers |= bit; } } /* * Legal modifier name. */ if (bp >= last_dash) { /* * Modifier with single letter, or special key name. */ if (modifiers != 0 && last_dash[2] == '>') key = last_dash[1]; else key = get_special_key_code(last_dash + 1); /* * get_special_key_code() may return NUL for invalid * special key name. */ if (key != NUL) { /* * Only use a modifier when there is no special key code that * includes the modifier. */ key = simplify_key(key, &modifiers); if (!keycode) { /* don't want keycode, use single byte code */ if (key == K_BS) key = BS; else if (key == K_DEL) key = DEL; } /* * Normal Key with modifier: Try to make a single byte code. */ if (!IS_SPECIAL(key)) { if ((modifiers & MOD_MASK_SHIFT) && isalpha(key)) { key = TO_UPPER(key); modifiers &= ~MOD_MASK_SHIFT; } if ((modifiers & MOD_MASK_CTRL) && ((key >= '?' && key <= '_') || isalpha(key))) { if (key == '?') key = DEL; else key &= 0x1f; modifiers &= ~MOD_MASK_CTRL; } if ((modifiers & MOD_MASK_ALT) && key < 0x80) { key |= 0x80; modifiers &= ~MOD_MASK_ALT; } } *modp = modifiers; *srcp = end_of_name; return key; } } } return 0; } /* * Try to find key "c" in the special key table. * Return the index when found, -1 when not found. */ int find_special_key_in_table(c) int c; { int i; for (i = 0; key_names_table[i].name != NULL; i++) if (c == key_names_table[i].key) break; if (key_names_table[i].name == NULL) i = -1; return i; } /* * Find the special key with the given name (the given string does not have to * end with NUL, the name is assumed to end before the first non-idchar). * If the name starts with "t_" the next two characters are interpreted as a * termcap name. * Return the key code, or 0 if not found. */ int get_special_key_code(name) char_u *name; { char_u *table_name; char_u string[3]; int i, j; /* * If it's we get the code for xx from the termcap */ if (name[0] == 't' && name[1] == '_' && name[2] != NUL && name[3] != NUL) { string[0] = name[2]; string[1] = name[3]; string[2] = NUL; if (add_termcap_entry(string, FALSE) == OK) return TERMCAP2KEY(name[2], name[3]); } else for (i = 0; key_names_table[i].name != NULL; i++) { table_name = key_names_table[i].name; for (j = 0; vim_isIDc(name[j]) && table_name[j] != NUL; j++) if (TO_LOWER(table_name[j]) != TO_LOWER(name[j])) break; if (!vim_isIDc(name[j]) && table_name[j] == NUL) return key_names_table[i].key; } return 0; } char_u * get_key_name(i) int i; { if (i >= KEY_NAMES_TABLE_LEN) return NULL; return key_names_table[i].name; } #ifdef USE_MOUSE /* * Look up the given mouse code to return the relevant information in the other * arguments. Return which button is down or was released. */ int get_mouse_button(code, is_click, is_drag) int code; int *is_click; int *is_drag; { int i; for (i = 0; mouse_table[i].pseudo_code; i++) if (code == mouse_table[i].pseudo_code) { *is_click = mouse_table[i].is_click; *is_drag = mouse_table[i].is_drag; return mouse_table[i].button; } return 0; /* Shouldn't get here */ } /* * Return the appropriate pseudo mouse event token (KE_LEFTMOUSE etc) based on * the given information about which mouse button is down, and whether the * mouse was clicked, dragged or released. */ int get_pseudo_mouse_code(button, is_click, is_drag) int button; /* eg MOUSE_LEFT */ int is_click; int is_drag; { int i; for (i = 0; mouse_table[i].pseudo_code; i++) if (button == mouse_table[i].button && is_click == mouse_table[i].is_click && is_drag == mouse_table[i].is_drag) { return mouse_table[i].pseudo_code; } return (int)KE_IGNORE; /* not recongnized, ignore it */ } #endif /* USE_MOUSE */ /* * Return the current end-of-line type: EOL_DOS, EOL_UNIX or EOL_MAC. */ int get_fileformat(buf) BUF *buf; { int c = *buf->b_p_ff; if (buf->b_p_bin || c == 'u') return EOL_UNIX; if (c == 'm') return EOL_MAC; return EOL_DOS; } /* * Set the current end-of-line type to EOL_DOS, EOL_UNIX or EOL_MAC. * Sets both 'textmode' and 'fileformat'. */ void set_fileformat(t) int t; { switch (t) { case EOL_DOS: set_string_option_direct((char_u *)"ff", -1, (char_u *)FF_DOS, TRUE); curbuf->b_p_tx = TRUE; break; case EOL_UNIX: set_string_option_direct((char_u *)"ff", -1, (char_u *)FF_UNIX, TRUE); curbuf->b_p_tx = FALSE; break; case EOL_MAC: set_string_option_direct((char_u *)"ff", -1, (char_u *)FF_MAC, TRUE); curbuf->b_p_tx = FALSE; break; } check_status(curbuf); } /* * Return the default fileformat from 'fileformats'. */ int default_fileformat() { switch (*p_ffs) { case 'm': return EOL_MAC; case 'd': return EOL_DOS; } return EOL_UNIX; } /* * Call shell. Calls mch_call_shell, with 'shellxquote' added. */ int call_shell(cmd, opt) char_u *cmd; int opt; { char_u *ncmd; #ifdef USE_GUI_WIN32 /* Don't hide the pointer while executing a shell command. */ gui_mch_mousehide(FALSE); #endif if (cmd == NULL || *p_sxq == NUL) call_shell_retval = mch_call_shell(cmd, opt); else { ncmd = alloc((unsigned)(STRLEN(cmd) + STRLEN(p_sxq) * 2 + 1)); if (ncmd != NULL) { STRCPY(ncmd, p_sxq); STRCAT(ncmd, cmd); STRCAT(ncmd, p_sxq); call_shell_retval = mch_call_shell(ncmd, opt); vim_free(ncmd); } else call_shell_retval = -1; } return call_shell_retval; } /* * VISUAL and OP_PENDING State are never set, they are equal to NORMAL State * with a condition. This function returns the real State. */ int get_real_state() { if ((State & NORMAL)) { if (VIsual_active) return VISUAL; else if (finish_op) return OP_PENDING; } return State; } /* * Change to a file's directory. */ int vim_chdirfile(fname) char_u *fname; { char_u temp_string[MAXPATHL]; char_u *p; char_u *t; STRCPY(temp_string, fname); p = get_past_head(temp_string); t = gettail(temp_string); while (t > p && vim_ispathsep(t[-1])) --t; *t = NUL; /* chop off end of string */ return mch_chdir((char *)temp_string); } #ifdef CURSOR_SHAPE /* * Handling of cursor shapes in various modes. */ struct cursor_entry cursor_table[SHAPE_COUNT] = { /* The values will be filled in from the guicursor' default when the GUI * starts. */ {0, 0, 700L, 400L, 250L, 0, "n"}, {0, 0, 700L, 400L, 250L, 0, "v"}, {0, 0, 700L, 400L, 250L, 0, "i"}, {0, 0, 700L, 400L, 250L, 0, "r"}, {0, 0, 700L, 400L, 250L, 0, "c"}, {0, 0, 700L, 400L, 250L, 0, "ci"}, {0, 0, 700L, 400L, 250L, 0, "cr"}, {0, 0, 100L, 100L, 100L, 0, "sm"}, {0, 0, 700L, 400L, 250L, 0, "o"}, {0, 0, 700L, 400L, 250L, 0, "ve"} }; /* * Parse the 'guicursor' option. * Returns error message for an illegal option, NULL otherwise. */ char_u * parse_guicursor() { char_u *modep; char_u *colonp; char_u *commap; char_u *p, *endp; int idx = 0; /* init for GCC */ int all_idx; int len; int i; long n; int found_ve = FALSE; /* found "ve" flag */ /* * Repeat for all comma separated parts. */ modep = p_guicursor; while (*modep) { colonp = vim_strchr(modep, ':'); if (colonp == NULL) return (char_u *)"Missing colon"; commap = vim_strchr(modep, ','); /* * Repeat for all mode's before the colon. * For the 'a' mode, we loop to handle all the modes. */ all_idx = -1; while (modep < colonp || all_idx >= 0) { if (all_idx < 0) { /* Find the mode. */ if (modep[1] == '-' || modep[1] == ':') len = 1; else len = 2; if (len == 1 && TO_LOWER(modep[0]) == 'a') all_idx = SHAPE_COUNT - 1; else { for (idx = 0; idx < SHAPE_COUNT; ++idx) if (STRNICMP(modep, cursor_table[idx].name, len) == 0) break; if (idx == SHAPE_COUNT) return (char_u *)"Illegal mode"; if (len == 2 && modep[0] == 'v' && modep[1] == 'e') found_ve = TRUE; } modep += len + 1; } if (all_idx >= 0) idx = all_idx--; else { /* Set the defaults, for the missing parts */ cursor_table[idx].shape = SHAPE_BLOCK; cursor_table[idx].blinkwait = 700L; cursor_table[idx].blinkon = 400L; cursor_table[idx].blinkoff = 250L; } /* Parse the part after the colon */ for (p = colonp + 1; *p && *p != ','; ) { /* * First handle the ones with a number argument. */ i = *p; len = 0; if (STRNICMP(p, "ver", 3) == 0) len = 3; else if (STRNICMP(p, "hor", 3) == 0) len = 3; else if (STRNICMP(p, "blinkwait", 9) == 0) len = 9; else if (STRNICMP(p, "blinkon", 7) == 0) len = 7; else if (STRNICMP(p, "blinkoff", 8) == 0) len = 8; if (len) { p += len; if (!isdigit(*p)) return (char_u *)"digit expected"; n = getdigits(&p); if (len == 3) /* "ver" or "hor" */ { if (n == 0) return (char_u *)"Illegal percentage"; if (TO_LOWER(i) == 'v') cursor_table[idx].shape = SHAPE_VER; else cursor_table[idx].shape = SHAPE_HOR; cursor_table[idx].percentage = n; } else if (len == 9) cursor_table[idx].blinkwait = n; else if (len == 7) cursor_table[idx].blinkon = n; else cursor_table[idx].blinkoff = n; } else if (STRNICMP(p, "block", 5) == 0) { cursor_table[idx].shape = SHAPE_BLOCK; p += 5; } else /* must be a highlight group name then */ { endp = vim_strchr(p, '-'); if (commap == NULL) /* last part */ { if (endp == NULL) endp = p + STRLEN(p); /* find end of part */ } else if (endp > commap || endp == NULL) endp = commap; cursor_table[idx].id = syn_check_group(p, (int)(endp - p)); p = endp; } if (*p == '-') ++p; } } modep = p; if (*modep == ',') ++modep; } /* If the 's' flag is not given, use the 'v' cursor for 's' */ if (!found_ve) { cursor_table[SHAPE_VE].shape = cursor_table[SHAPE_V].shape; cursor_table[SHAPE_VE].percentage = cursor_table[SHAPE_V].percentage; cursor_table[SHAPE_VE].blinkwait = cursor_table[SHAPE_V].blinkwait; cursor_table[SHAPE_VE].blinkon = cursor_table[SHAPE_V].blinkon; cursor_table[SHAPE_VE].blinkoff = cursor_table[SHAPE_V].blinkoff; cursor_table[SHAPE_VE].id = cursor_table[SHAPE_V].id; } return NULL; } /* * Return the index into cursor_table[] for the current mode. */ int get_cursor_idx() { if (State == SHOWMATCH) return SHAPE_SM; if (State == INSERT) return SHAPE_I; if (State == REPLACE) return SHAPE_R; if (State == CMDLINE) { if (cmdline_at_end()) return SHAPE_C; if (cmdline_overstrike()) return SHAPE_CR; return SHAPE_CI; } if (finish_op) return SHAPE_O; if (VIsual_active) { if (*p_sel == 'e') return SHAPE_VE; else return SHAPE_V; } return SHAPE_N; } #endif /* CURSOR_SHAPE */