📄 eval.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.
*/
/*
* eval.c: Expression evaluation.
*/
#include "vim.h"
#ifdef HAVE_FCNTL_H
# include <fcntl.h> /* contains setenv() declaration for Amiga */
#endif
#ifdef AMIGA
# include <time.h> /* for strftime() */
#endif
#ifdef WANT_EVAL
/*
* Structure to hold an internal variable.
*/
typedef struct
{
char_u *var_name; /* name of variable */
char var_type; /* VAR_NUMBER or VAR_STRING */
union
{
#if SIZEOF_INT <= 3 /* use long if int is smaller than 32 bits */
long var_number; /* number value */
#else
int var_number; /* number value */
#endif
char_u *var_string; /* string value (Careful: can be NULL!) */
} var_val;
} var;
#define VAR_UNKNOWN 0
#define VAR_NUMBER 1
#define VAR_STRING 2
typedef var * VAR;
/*
* All user-defined internal variables are stored in "variables".
*/
struct growarray variables = {0, 0, sizeof(var), 4, NULL};
#define VAR_ENTRY(idx) (((VAR)(variables.ga_data))[idx])
#define VAR_GAP_ENTRY(idx, gap) (((VAR)(gap->ga_data))[idx])
#define BVAR_ENTRY(idx) (((VAR)(curbuf->b_vars.ga_data))[idx])
#define WVAR_ENTRY(idx) (((VAR)(curwin->w_vars.ga_data))[idx])
static int echo_attr = 0; /* attributes used for ":echo" */
/*
* Structure to hold info for a user function.
*/
struct ufunc
{
struct ufunc *next; /* next function in list */
char_u *name; /* name of function */
int varargs; /* variable nr of arguments */
int flags;
struct growarray args; /* arguments */
struct growarray lines; /* function lines */
};
/* function flags */
#define FC_ABORT 1 /* abort function on error */
#define FC_RANGE 2 /* function accepts range */
/*
* All user-defined functions are found in the forward-linked function list.
* The first function is pointed at by firstfunc.
*/
struct ufunc *firstfunc = NULL;
#define FUNCARG(fp, j) ((char_u **)(fp->args.ga_data))[j]
#define FUNCLINE(fp, j) ((char_u **)(fp->lines.ga_data))[j]
/* structure to hold info for a function that is currently being executed. */
struct funccall
{
struct ufunc *func; /* function being called */
int linenr; /* next line to be executed */
int argcount; /* nr of arguments */
VAR argvars; /* arguments */
var a0_var; /* "a:0" variable */
var firstline; /* "a:firstline" variable */
var lastline; /* "a:lastline" variable */
struct growarray l_vars; /* local function variables */
VAR retvar; /* return value variable */
};
/* pointer to funccal for currently active function */
struct funccall *current_funccal = NULL;
static char_u *cat_prefix_varname __ARGS((int prefix, char_u *name));
static int eval0 __ARGS((char_u *arg, VAR retvar, char_u **nextcmd));
static int eval1 __ARGS((char_u **arg, VAR retvar));
static int eval2 __ARGS((char_u **arg, VAR retvar));
static int eval3 __ARGS((char_u **arg, VAR retvar));
static int eval4 __ARGS((char_u **arg, VAR retvar));
static int eval5 __ARGS((char_u **arg, VAR retvar));
static int eval6 __ARGS((char_u **arg, VAR retvar));
static int get_option_var __ARGS((char_u **arg, VAR retvar));
static int get_string_var __ARGS((char_u **arg, VAR retvar));
static int get_lit_string_var __ARGS((char_u **arg, VAR retvar));
static int get_env_var __ARGS((char_u **arg, VAR retvar));
static int get_func_var __ARGS((char_u *name, int len, VAR retvar, char_u **arg, linenr_t firstline, linenr_t lastline, int *doesrange));
static void f_argc __ARGS((VAR argvars, VAR retvar));
static void f_argv __ARGS((VAR argvars, VAR retvar));
static void f_browse __ARGS((VAR argvars, VAR retvar));
static void f_bufexists __ARGS((VAR argvars, VAR retvar));
static BUF *get_buf_var __ARGS((VAR avar));
static void f_bufname __ARGS((VAR argvars, VAR retvar));
static void f_bufnr __ARGS((VAR argvars, VAR retvar));
static void f_char2nr __ARGS((VAR argvars, VAR retvar));
static void f_col __ARGS((VAR argvars, VAR retvar));
static void f_confirm __ARGS((VAR argvars, VAR retvar));
static void f_delete __ARGS((VAR argvars, VAR retvar));
static void f_escape __ARGS((VAR argvars, VAR retvar));
static void f_exists __ARGS((VAR argvars, VAR retvar));
static void f_expand __ARGS((VAR argvars, VAR retvar));
static void f_filereadable __ARGS((VAR argvars, VAR retvar));
static void f_fnamemodify __ARGS((VAR argvars, VAR retvar));
static void f_getcwd __ARGS((VAR argvars, VAR retvar));
static void f_getline __ARGS((VAR argvars, VAR retvar));
static void f_has __ARGS((VAR argvars, VAR retvar));
static void f_hlexists __ARGS((VAR argvars, VAR retvar));
static void f_hlID __ARGS((VAR argvars, VAR retvar));
static void f_hostname __ARGS((VAR argvars, VAR retvar));
static void f_isdirectory __ARGS((VAR argvars, VAR retvar));
static void f_input __ARGS((VAR argvars, VAR retvar));
static void f_last_buffer_nr __ARGS((VAR argvars, VAR retvar));
static void f_line __ARGS((VAR argvars, VAR retvar));
static void f_match __ARGS((VAR argvars, VAR retvar));
static void f_matchend __ARGS((VAR argvars, VAR retvar));
static void f_matchstr __ARGS((VAR argvars, VAR retvar));
static void f_nr2char __ARGS((VAR argvars, VAR retvar));
static void f_setline __ARGS((VAR argvars, VAR retvar));
static void f_some_match __ARGS((VAR argvars, VAR retvar, int start));
static void f_strftime __ARGS((VAR argvars, VAR retvar));
static void f_strlen __ARGS((VAR argvars, VAR retvar));
static void f_strpart __ARGS((VAR argvars, VAR retvar));
static void f_synID __ARGS((VAR argvars, VAR retvar));
static void f_synIDattr __ARGS((VAR argvars, VAR retvar));
static void f_synIDtrans __ARGS((VAR argvars, VAR retvar));
static void f_substitute __ARGS((VAR argvars, VAR retvar));
static void f_tempname __ARGS((VAR argvars, VAR retvar));
static void f_virtcol __ARGS((VAR argvars, VAR retvar));
static void f_winbufnr __ARGS((VAR argvars, VAR retvar));
static void f_winheight __ARGS((VAR argvars, VAR retvar));
static void f_winnr __ARGS((VAR argvars, VAR retvar));
static WIN *find_win_by_nr __ARGS((VAR vp));
static FPOS *var2fpos __ARGS((VAR varp));
static int get_env_len __ARGS((char_u **arg));
char_u *get_env_string __ARGS((char_u **arg));
static int get_id_len __ARGS((char_u **arg));
static int eval_isnamec __ARGS((int c));
static int get_var_var __ARGS((char_u *name, int len, VAR retvar));
static VAR alloc_var __ARGS((void));
static VAR alloc_string_var __ARGS((char_u *string));
static void free_var __ARGS((VAR varp));
static void clear_var __ARGS((VAR varp));
static long get_var_number __ARGS((VAR varp));
static char_u *get_var_string __ARGS((VAR varp));
static char_u *get_var_string_buf __ARGS((VAR varp, char_u *buf));
static VAR find_var __ARGS((char_u *name, int writing));
static struct growarray *find_var_ga __ARGS((char_u *name, char_u **varname));
static void var_free_one __ARGS((VAR v));
static void list_one_var __ARGS((VAR v, char_u *prefix));
static void set_var __ARGS((char_u *name, VAR varp));
static char_u *find_option_end __ARGS((char_u *p));
static void list_func_head __ARGS((struct ufunc *fp));
static struct ufunc *find_func __ARGS((char_u *name));
static void call_func __ARGS((struct ufunc *fp, int argcount, VAR argvars, VAR retvar, linenr_t firstline, linenr_t lastline));
/*
* Set an internal variable to a string value. Creates the variable if it does
* not already exist.
*/
void
set_internal_string_var(name, value)
char_u *name;
char_u *value;
{
char_u *val;
VAR varp;
val = vim_strsave(value);
if (val != NULL)
{
varp = alloc_string_var(val);
if (varp != NULL)
{
set_var(name, varp);
free_var(varp);
}
}
}
/*
* Top level evaluation function, returning a boolean.
* Sets "error" to TRUE if there was an error.
* Return TRUE or FALSE.
*/
int
eval_to_bool(arg, error, nextcmd)
char_u *arg;
int *error;
char_u **nextcmd;
{
var retvar;
int retval;
if (eval0(arg, &retvar, nextcmd) == FAIL)
{
*error = TRUE;
retval = FALSE;
}
else
{
*error = FALSE;
retval = (get_var_number(&retvar) != 0);
clear_var(&retvar);
}
return retval;
}
/*
* Top level evaluation function, returning a string.
* Return pointer to allocated memory, or NULL for failure.
*/
char_u *
eval_to_string(arg, nextcmd)
char_u *arg;
char_u **nextcmd;
{
var retvar;
char_u *retval;
if (eval0(arg, &retvar, nextcmd) == FAIL)
retval = NULL;
else
{
retval = vim_strsave(get_var_string(&retvar));
clear_var(&retvar);
}
return retval;
}
/*
* ":let var = expr" assignment command.
* ":let var" list one variable value
* ":let" list all variable values
*/
void
do_let(eap)
EXARG *eap;
{
char_u *arg = eap->arg;
char_u *expr;
char_u *name;
VAR varp;
var retvar;
char_u *p;
int c1, c2;
int i;
#ifndef HAVE_SETENV
char_u *envbuf;
#endif
expr = vim_strchr(arg, '=');
if (expr == NULL)
{
if (ends_excmd(*arg))
{
if (!eap->skip)
{
/*
* List all variables.
*/
for (i = 0; i < variables.ga_len; ++i)
if (VAR_ENTRY(i).var_name != NULL)
list_one_var(&VAR_ENTRY(i), (char_u *)"");
for (i = 0; i < curbuf->b_vars.ga_len; ++i)
if (BVAR_ENTRY(i).var_name != NULL)
list_one_var(&BVAR_ENTRY(i), (char_u *)"b:");
for (i = 0; i < curwin->w_vars.ga_len; ++i)
if (WVAR_ENTRY(i).var_name != NULL)
list_one_var(&WVAR_ENTRY(i), (char_u *)"w:");
}
}
else
{
/*
* List variables.
*/
while (!ends_excmd(*arg))
{
for (p = arg; eval_isnamec(*p); ++p)
;
if (!vim_iswhite(*p) && !ends_excmd(*p))
{
EMSG(e_trailing);
break;
}
if (!eap->skip)
{
c1 = *p;
*p = NUL;
varp = find_var(arg, FALSE);
if (varp == NULL)
EMSG2("Unknown variable: \"%s\"", arg);
else
{
name = vim_strchr(arg, ':');
if (name != NULL)
{
c2 = *++name;
*name = NUL;
list_one_var(varp, arg);
*name = c2;
}
else
list_one_var(varp, (char_u *)"");
}
*p = c1;
}
arg = skipwhite(p);
}
}
eap->nextcmd = check_nextcmd(arg);
}
else
{
if (eap->skip)
++emsg_off;
i = eval0(expr + 1, &retvar, &eap->nextcmd);
if (eap->skip)
{
if (i != FAIL)
clear_var(&retvar);
--emsg_off;
}
else if (i != FAIL)
{
/*
* ":let $VAR = expr": Set environment variable.
*/
if (*arg == '$')
{
int len;
int cc;
/* Find the end of the name. */
++arg;
name = arg;
len = get_env_len(&arg);
if (name != NULL)
{
if (*skipwhite(arg) != '=')
EMSG(e_letunexp);
else
{
cc = name[len];
name[len] = NUL;
p = get_var_string(&retvar);
#ifdef HAVE_SETENV
mch_setenv((char *)name, (char *)p, 1);
#else
/*
* Putenv does not copy the string, it has to remain
* valid. The allocated memory will never be freed.
*/
envbuf = alloc((unsigned)(STRLEN(p) +
STRLEN(name) + 2));
if (envbuf != NULL)
{
sprintf((char *)envbuf, "%s=%s", name, p);
putenv((char *)envbuf);
}
#endif
if (STRICMP(name, "home") == 0)
init_homedir();
name[len] = cc;
}
}
}
/*
* ":let &option = expr": Set option value.
*/
else if (*arg == '&')
{
/*
* Find the end of the name;
*/
++arg;
p = find_option_end(arg);
if (*skipwhite(p) != '=')
EMSG(e_letunexp);
else
{
c1 = *p;
*p = NUL;
set_option_value(arg, get_var_number(&retvar),
get_var_string(&retvar));
*p = c1; /* put back for error messages */
}
}
/*
* ":let @r = expr": Set register contents.
*/
else if (*arg == '@')
{
++arg;
if (*skipwhite(arg + 1) != '=')
EMSG(e_letunexp);
else
write_reg_contents(*arg == '@' ? '"' : *arg,
get_var_string(&retvar));
}
/*
* ":let var = expr": Set internal variable.
*/
else if (eval_isnamec(*arg) && !isdigit(*arg))
{
/*
* Find the end of the name;
*/
for (p = arg; eval_isnamec(*p); ++p)
;
if (*skipwhite(p) != '=')
EMSG(e_letunexp);
else
{
c1 = *p;
*p = NUL;
set_var(arg, &retvar);
*p = c1; /* put char back for error messages */
}
}
else
{
EMSG2(e_invarg2, arg);
}
clear_var(&retvar);
}
}
}
/*
* ":1,25call func(arg1, arg2)" function call.
*/
void
do_call(eap)
EXARG *eap;
{
char_u *arg = eap->arg;
char_u *startarg;
char_u *name;
var retvar;
int len;
linenr_t lnum;
int doesrange;
name = arg;
len = get_id_len(&arg);
startarg = arg;
if (*startarg != '(')
{
EMSG2("Missing braces: %s", name);
return;
}
/*
* When skipping, evaluate the function once, to find the end of the
* arguments.
* When the function takes a range, this is discovered after the first
* call, and the loop is broken.
*/
if (eap->skip)
++emsg_off;
for (lnum = eap->line1; lnum <= eap->line2; ++lnum)
{
if (!eap->skip && eap->line1 != eap->line2)
{
curwin->w_cursor.lnum = lnum;
curwin->w_cursor.col = 0;
}
arg = startarg;
if (get_func_var(name, len, &retvar, &arg,
eap->line1, eap->line2, &doesrange) == FAIL)
break;
clear_var(&retvar);
if (doesrange || eap->skip)
break;
}
if (eap->skip)
--emsg_off;
eap->nextcmd = check_nextcmd(arg);
}
/*
* ":unlet[!] var1 ... " command.
*/
void
do_unlet(arg, forceit)
char_u *arg;
int forceit;
{
char_u *name_end;
VAR v;
char_u cc;
do
{
name_end = skiptowhite(arg);
cc = *name_end;
*name_end = NUL;
v = find_var(arg, TRUE);
if (v != NULL) /* existing variable, may need to free string */
var_free_one(v);
else if (!forceit) /* non-existing variable */
{
*name_end = cc;
EMSG2("No such variable: \"%s\"", arg);
break;
}
*name_end = cc;
arg = skipwhite(name_end);
} while (*arg != NUL);
}
/*
* Local string buffer for the next two functions to store a variable name
* with its prefix. Allocated in cat_prefix_varname(), freed later in
* get_user_var_name().
*/
static char_u *varnamebuf = NULL;
static int varnamebuflen = 0;
/*
* Function to concatenate a prefix and a variable name.
* TODO: Use string instead of char for 'prefix' when allowing 'b3:' like vars.
*/
static char_u *
cat_prefix_varname(prefix, name)
int prefix;
char_u *name;
{
int len;
len = STRLEN( name ) + 3;
if ( len > varnamebuflen )
{
vim_free(varnamebuf);
len += 10; /* some additional space */
varnamebuf = alloc(len);
if (varnamebuf)
varnamebuflen = len;
else
{
varnamebuflen = 0;
return NULL;
}
}
*varnamebuf = prefix;
varnamebuf[1] = ':';
STRCPY(varnamebuf+2, name);
return varnamebuf;
}
/*
* Function given to ExpandGeneric() to obtain the list of user defined
* (global/buffer/window) variable names.
*/
char_u *
get_user_var_name(idx)
int idx;
{
if (idx < variables.ga_len) /* Global variables */
return VAR_ENTRY(idx).var_name;
/* Current buffer variables */
else if ((idx -= variables.ga_len) < curbuf->b_vars.ga_len)
return cat_prefix_varname('b', BVAR_ENTRY(idx).var_name);
/* Current window variables */
else if ((idx -= curbuf->b_vars.ga_len) < curwin->w_vars.ga_len)
return cat_prefix_varname('w', WVAR_ENTRY(idx).var_name);
else
{
vim_free(varnamebuf);
varnamebuf = NULL;
varnamebuflen = 0;
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -